home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
1122
/
1122.xpi
/
chrome
/
tabmixplus.jar
/
content
/
tabmixplus
/
session
/
session.js
< prev
next >
Wrap
Text File
|
2009-10-11
|
160KB
|
3,470 lines
// we call initDATASource also from pref-tabmix.js
// don't move this to another file
if (!("gIOService" in window)) {
__defineGetter__("gIOService", function() {
delete this.gIOService;
return this.gIOService = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
});
}
const gRDFRoot = "rdf://tabmix";
const HSitems = 3; // in history we have title, url, scrollpos
var NC_TM = [];
var gSessionPath = ["", "", "", ""];
var nsIPrefServiceObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
var SessionPref = nsIPrefServiceObj.getBranch("extensions.tabmix.sessions.");
var gThisWin = null;
var gThisWinTabs = null;
var gThisWinClosedtabs = null;
var tmSHEntry = Components.interfaces.nsISHEntry;
var tmSHistory = Components.interfaces.nsISHistory;
var tmRDFResource = Components.interfaces.nsIRDFResource;
const BUTTON_OK = 0;
const BUTTON_CANCEL = 1;
const BUTTON_EXTRA1 = 2;
const SHOW_MENULIST = 1;
const SHOW_TEXTBOX = 0;
const HIDE_MENUANDTEXT = 2;
const CHECKBOX_UNCHECKED = 0;
const CHECKBOX_CHECKED = 1;
const HIDE_CHECKBOX = 2;
const SELECT_DEFAULT = 0;
const SELECT_LASTSESSION = 1;
const SELECT_CRASH = 2;
const SHOW_CLOSED_WINDOW_LIST = 3;
const DLG_SAVE = 0;
const DLG_RENAME = 1;
const NO_NEED_TO_REPLACE = -1;
function tmLog(aMessage, aShowCaller) {
var tmConsoleService = Components.classes['@mozilla.org/consoleservice;1']
.getService(Components.interfaces.nsIConsoleService);
try {
var caller = arguments.callee.caller;
var callerName = caller.name;
var callerCallerName = "";
if (aShowCaller) {
var prevCaller = caller.caller;
var prevCalerName = prevCaller && "name" in prevCaller ? prevCaller.name : prevCaller || "?" ;
callerCallerName = aShowCaller ? " (caller was " + prevCalerName + ")" : "";
}
} catch (e) {tmLog(e); callerCallerName = callerName ="";}
tmConsoleService.logStringMessage("TabMix " + callerName + callerCallerName + ":\n" + aMessage );
}
//TMP_objectProperties
function object2String(_obj, aMessage, aDisallowLog) {
aMessage = aMessage ? aMessage : "";
var _objS = _obj ? _obj.toString() : "_obj is " + typeof(_obj);
_objS += ":\n"
try {
for (var item in _obj) {
var _val = _obj[item];
_objS += item + "[" + typeof(_val) + "]" + " = " + _val + "\n";
if (typeof(_val) == "object") {
_objS += "\n";
}
}
} catch (er) { _objS += item + " = " + "error in this item" + "\n";}
if (aDisallowLog)
_objS = aMessage + "\n======================\n" + _objS
else
tmLog(aMessage + "\n=============== Object Properties ===============\n" + _objS);
return _objS;
}
function TMP_ASSERT(aError, aMsg) {
var caller = arguments.callee.caller;
var callerName = caller.name;
var errAt = callerName ? " at " + callerName : ""
var assertionText = "Tabmix Plus ERROR" + errAt + ":\n" + (aMsg ? aMsg + "\n" : "") + aError + "\n";
var stackText = "stack" in aError ? "Stack Trace: \n" + aError.stack : "";
var csv = Components.classes['@mozilla.org/consoleservice;1']
.getService(Components.interfaces.nsIConsoleService);
csv.logStringMessage(assertionText + stackText);
}
function getObject(aObj, aMethod) {
aObj = aObj[aMethod.shift()];
if (aMethod.length)
aObj = getObject(aObj, aMethod);
return aObj;
}
function showMethod(aMethod, aDelay) {
try {
if (typeof(aDelay) == "undefined")
aDelay = 500;
var methodsList = aMethod.split(".");
if (methodsList[0] == "window")
methodsList.shift();
if (aDelay >= 0)
window.setTimeout(function () { tmLog(aMethod + " " + getObject(window || getTopWin(), methodsList).toString()); }, aDelay);
else
tmLog(aMethod + " " + getObject(window, methodsList).toString());
} catch (ex) {TMP_ASSERT(ex, "Error we can't show " + aMethod + " in showMethod");}
}
/*
sanitize privte data by delete the files session.rdf session.old
*/
var TMP_Sanitizer = {
_prefService: null,
get prefService() {
if (!this._prefService) {
this._prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
}
return this._prefService;
},
_isFirefox35: null,
get isFirefox35() {
if (this._isFirefox35 == null) {
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
.getService(Components.interfaces.nsIVersionComparator);
this._isFirefox35 = versionChecker.compare(appInfo.version, "3.1a2") > 0;
}
return this._isFirefox35;
},
addSanitizeItem: function () {
if (typeof Sanitizer != 'function')
return;
// Sanitizer will execute this
Sanitizer.prototype.items['extensions-tabmix'] = {
clear : function() {
try {
TMP_Sanitizer.sanitize();
} catch (ex) {
try { Components.utils.reportError(ex); } catch(ex) {}
}
},
get canClear() {
return true;
}
}
},
addMenuItem: function () {
var prefs = document.getElementsByTagName("preferences")[0];
var _item;
var itemList = document.getElementById("itemList");
if (itemList)
_item = itemList.lastChild;
else {
_item = document.getElementsByTagName("checkbox");
_item = _item[this.isFirefox35 ? _item.length - 1 : 0];
}
if (prefs && _item) {// if this isn't true we are lost :)
var prefName;
if (this.isFirefox35) {
var cpd = _item.getAttribute("preference").indexOf("privacy.cpd.") != -1;
if (cpd)
prefName = "privacy.cpd.extensions-tabmix";
else
prefName = "privacy.clearOnShutdown.extensions-tabmix";
}
else
prefName = "privacy.item.extensions-tabmix";
var pref = document.createElement("preference");
pref.setAttribute("id", prefName);
pref.setAttribute("name", prefName);
pref.setAttribute("type", "bool");
prefs.appendChild(pref);
var check = document.createElement(itemList ? "listitem" : "checkbox");
check.setAttribute("label", tabmixSanitize.label);
check.setAttribute("accesskey", tabmixSanitize.accesskey);
check.setAttribute("preference", prefName);
check.setAttribute("oncommand", "TMP_Sanitizer.confirm(this);");
if (itemList) {
check.setAttribute("type", "checkbox");
itemList.appendChild(check);
}
else
_item.parentNode.insertBefore(check, this.isFirefox35 ? null : _item);
if (typeof(gSanitizePromptDialog) == "object") {
pref.setAttribute("readonly", "true");
check.setAttribute("onsyncfrompreference", "return gSanitizePromptDialog.onReadGeneric();");
}
}
},
confirm: function (aCheckbox) {
if (!aCheckbox.checked)
return;
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var title = "Tab Mix Plus - " + document.title;
var msg = tabmixSanitize.confirm;
var buttonPressed = promptService.confirmEx(null,
title,
msg,
(promptService.BUTTON_TITLE_YES * promptService.BUTTON_POS_0)
+ (promptService.BUTTON_TITLE_NO * promptService.BUTTON_POS_1),
null, null, null, null, {});
if (buttonPressed == 1)
aCheckbox.checked = false;
},
isSanitizeTMPwithoutPrompet: function (aOnExit) {
/*
* from Firefox 3.5 privacy.sanitize.promptOnSanitize was removed
*
* the new behavior is:
* - Tools > Clear Recent History... - Always show the UI
* - about:privatebrowsing clearing your recent history - Always show the UI
* - clear private data on exit - NEVER show the UI
*/
var promptOnSanitize;
if (this.isFirefox35)
promptOnSanitize = !aOnExit;
else {
try {
promptOnSanitize = this.prefService.getBoolPref("privacy.sanitize.promptOnSanitize");
} catch (e) { promptOnSanitize = true;}
}
// if promptOnSanitize is true we call TMP_Sanitizer.sanitize from Firefox Sanitizer
if (promptOnSanitize)
return false;
try {
var sanitizeTabmix = this.prefService.getBoolPref("privacy.item.extensions-tabmix");
} catch (e) { sanitizeTabmix = false;}
return sanitizeTabmix;
},
tryToSanitize: function (aOnExit) {
if (this.isSanitizeTMPwithoutPrompet(aOnExit)) {
this.sanitize();
return true;
}
// History sanitize remove closed tab data from session restore
// we need to update closed tab button state
var w = getTopWin();
if (w && w.TMP_ClosedTabs.ssIsON) {
var _windows = windowEnumerator();
while ( _windows.hasMoreElements() ) {
_windows.getNext().TMP_ClosedTabs.setButtonDisableState();
}
}
return false;
},
// XXX need to add test if we fail to delete then alert the user or ....?
sanitize: function TMP_SN_sanitize() {
// get file references
var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var sessionFile = dirService.get("ProfD", Components.interfaces.nsILocalFile);
var sessionFileBackup = sessionFile.clone();
var sessionsBackupDir = sessionFile.clone()
sessionFile.append("session.rdf");
sessionFileBackup.append("session.old");
sessionsBackupDir.append("sessionbackups");
// remove the files from the disk
this.clearDisk(sessionFile);
this.clearDisk(sessionFileBackup);
this.clearDisk(sessionsBackupDir);
// init new DATASource for all open window
var enumerator = windowEnumerator();
var wnd, _sessionManager, broadcaster, btn;
while ( enumerator.hasMoreElements() ) {
wnd = enumerator.getNext();
// clear DATASource
delete wnd.gBrowser.windowID;
_sessionManager = wnd.SessionManager;
_sessionManager.corruptedFile = false;
_sessionManager.RDFService.UnregisterDataSource(_sessionManager.DATASource);
// init new DATASource
_sessionManager.initDATASource();
// disable closed window list button
broadcaster = wnd.document.getElementById("tmp_closedwindows");
if (broadcaster)
broadcaster.setAttribute("disabled",true);
// clear closed tabs and disable the button if we use TMP session manager and save close tabs
if ((SessionManager.enableManager || SessionManager.enableBackup) && SessionManager.saveClosedTabs) {
wnd.TMP_ClosedTabs.restoreTab("original", -1);
wnd.TMP_ClosedTabs.setButtonDisableState();
}
}
// set flag for next start
SessionPref.setBoolPref("sanitized" , true);
window.setTimeout( function () {
SessionManager.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "sanitized");
// Make sure to break session manager cycle with the save timer
if (SessionManager._saveTimer) {
SessionManager._saveTimer.cancel();
SessionManager._saveTimer = null;
}
SessionManager.saveState();
}, 0 );
},
clearDisk: function (aFile) {
if (aFile.exists()) {
try {
aFile.remove(aFile.isDirectory());
}
catch (ex) { dump(ex + "\n"); TMP_ASSERT(ex);} // couldn't remove the file - what now?
}
}
}
var SessionData = {
docShellItems: ["Images","Subframes","MetaRedirects","Plugins","Javascript"],
tabAttribute: ["protected","locked"],
getTabProperties: function sData_getTabProperties(aTab, checkPref) {
if (typeof(checkPref) == "undefined") checkPref = false; // pref check is only for session manager
var tabProperties = "", temp;
for ( var j = 0; j < this.tabAttribute.length; j++ ){
temp = aTab.hasAttribute(this.tabAttribute[j]) ? aTab.getAttribute(this.tabAttribute[j]) : "false";
tabProperties += (temp=="true") ? "1" : "0";
}
// if save.permissions is false we save all Permissions as on, so if we change this pref after session
// was saved, the session will load with Permissions as on.
if (checkPref && !SessionPref.getBoolPref("save.permissions"))
tabProperties += "11111";
else {
var aTabDocShell = gBrowser.getBrowserForTab(aTab).docShell;
for ( j = 0; j < this.docShellItems.length; j++ ){
tabProperties += aTabDocShell["allow" + this.docShellItems[j]] ? "1" : "0";
}
}
// save fixed label data
if (aTab.hasAttribute("fixed-label")) {
temp = " fixed-label=" + encodeURI(aTab.getAttribute("fixed-label"));
temp += " label-uri=" + encodeURI(aTab.getAttribute("label-uri"));
tabProperties += temp;
}
// save reload data
if (aTab.getAttribute("reload-data")) {
tabProperties += " reload-data=" + encodeURI(aTab.getAttribute("reload-data"));
}
// save data for bookmark tab title
if (aTab.getAttribute("tabmix_bookmarkId")) {
tabProperties += " tabmix_bookmarkId=" + encodeURI(aTab.getAttribute("tabmix_bookmarkId"));
}
return tabProperties;
},
setTabProperties: function(aTab, tabProperties, checkPref) {
var booleanAttrLength = this.tabAttribute.length + this.docShellItems.length;
if (tabProperties.length > booleanAttrLength) {
var tabData = {xultab: ""};
tabData.xultab = tabProperties.substr(booleanAttrLength + 1);
var fixedLabel = TMP_SessionStore._getAttribute(tabData, "fixed-label");
if (fixedLabel) {
aTab.setAttribute("fixed-label", fixedLabel);
aTab.setAttribute("label-uri", TMP_SessionStore._getAttribute(tabData, "label-uri"));
}
var reloadData = TMP_SessionStore._getAttribute(tabData, "reload-data");
if (reloadData) {
aTab.setAttribute("reload-data", reloadData);
reloadData = reloadData.split(" ");
setupAutoReload(aTab);
aTab.autoReloadEnabled = true;
aTab.autoReloadURI = reloadData[0];
aTab.autoReloadTime = reloadData[1];
}
var bmitemid = TMP_SessionStore._getAttribute(tabData, "tabmix_bookmarkId");
if (bmitemid) {
aTab.setAttribute("tabmix_bookmarkId", bmitemid);
}
}
if (typeof(checkPref) == "undefined") checkPref = false; // pref check is only for session manager
tabProperties = tabProperties.substr(0, booleanAttrLength);
var k = this.tabAttribute.length;
for ( var j = 0; j < k; j++ ){
//extensions.tabmix.sessions.save.protected && extensions.tabmix.sessions.save.locked
var attrib = this.tabAttribute[j];
if (!checkPref || SessionPref.getBoolPref("save." + attrib)) {
TMP_setItem(aTab, attrib, tabProperties.charAt(j) == "1" || null);
}
}
if (alwaysNewTab == 1 || aTab.hasAttribute("locked"))
TMP_setItem(aTab, "_locked", aTab.hasAttribute("locked"));
if (checkPref && !SessionPref.getBoolPref("save.permissions")) return;
var aPermission;
var aTabDocShell = gBrowser.getBrowserForTab(aTab).docShell;
for ( j = 0; j < this.docShellItems.length; j++ ) {
aPermission = tabProperties.charAt(j + k) == "1";
if (aTabDocShell["allow" + this.docShellItems[j]] != aPermission)
aTabDocShell["allow" + this.docShellItems[j]] = aPermission;
}
}
}
function windowEnumerator(aWindowtype) {
if (typeof(aWindowtype) == "undefined")
aWindowtype = "navigator:browser";
var windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1']
.getService(Components.interfaces.nsIWindowMediator);
return windowMediator.getEnumerator(aWindowtype);
}
function numberOfWindows(all, aWindowtype) {
var enumerator = windowEnumerator(aWindowtype);
var count = 0;
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
if ("SessionManager" in win && win.SessionManager.windowClosed)
continue;
count++;
if (!all && count == 2)
break;
}
return count;
}
var SessionManager = {
RDFService: null,
CONUtils: null,
DATASource: null,
IOService: null,
overwriteWindow: false,
saveThisWindow: true,
NC_NS : "http://home.netscape.com/NC-rdf#",
enableBackup: null,
enableManager: null,
enableSaveHistory: null,
saveClosedtabs: null,
corruptedFile: false,
afterTabSwap: false,
// whether we are in private browsing mode (from firefox 3.5)
_inPrivateBrowsing: false,
// call by TM_init
init: function SM_init() {
var _afterTabduplicated = "tabmix_afterTabduplicated" in window && window.tabmix_afterTabduplicated;
var isFirstWindow = numberOfWindows() == 1 && !_afterTabduplicated;
/* Add attribute to nsSessionStore persistTabAttribute after delay
we call this after nsSessionStore.init
we add this also when we use TMP session manager.
we use Firefox SessionStore closed tab service and for restore after restart
*/
if (isFirstWindow)
TMP_SessionStore.persistTabAttribute();
// private browsing - from firefox 3.5
// closedwindows menu - from firefox 3.5
if (gIsFirefox35) {
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
observerService.addObserver(this, "browser-window-change-state", true);
observerService.addObserver(this, "private-browsing", true);
observerService.notifyObservers(null, "browser-window-change-state", "opened");
observerService.addObserver(this, "quit-application-requested", true);
observerService.addObserver(this, "browser-lastwindow-close-requested", true);
}
this.enableManager = SessionPref.getBoolPref("manager") && !this._inPrivateBrowsing;
this.enableBackup = SessionPref.getBoolPref("crashRecovery") && !this._inPrivateBrowsing;
this.enableSaveHistory = SessionPref.getBoolPref("save.history");
this.saveClosedtabs = SessionPref.getBoolPref("save.closedtabs") &&
tabxPrefs.getBoolPref("undoClose");
this._lastSaveTime = Date.now();
// check if we need to backup
if (isFirstWindow && this.enableManager && !SessionPref.prefHasUserValue("sanitized")) {
try {
this.archiveSessions();
}
catch (ex) {TMP_ASSERT(ex);}
}
if (!this.DATASource || isFirstWindow)
this.initService();
if (this._inPrivateBrowsing) {
this.updateSettings();
this.setLiteral(gThisWin, "dontLoad", "true");
return;
}
var path, status, caller, crashed;
if (isFirstWindow) {
path = gRDFRoot + "/closedSession/thisSession";
status = this.getLiteralValue(path, "status");
crashed = status.indexOf("crash") != -1;
// if this isn't delete on exit, we know next time that firefox crash
SessionPref.setBoolPref("crashed" , true); // we use this in setup.js;
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
this.setLiteral(path, "status", "crash");
// if we after sanitize, we have no data to restore
if (this.enableManager && SessionPref.prefHasUserValue("sanitized")) {
SessionPref.clearUserPref("sanitized");
this.loadHomePage();
this.saveStateDelayed();
return;
}
if (!this.enableManager && (!this.enableBackup || !crashed)) {
return;
}
if (crashed)
this.openAfterCrash(status);
else if (this.enableManager) {
// check if sessionStore restore the session after restart we do not need to do anything
var afterRestart = TMP_SessionStore.isAfterRestart();
if (afterRestart)
this.onSessionRestored();
else
this.openFirstWindow(false);
}
if (tabxPrefs.prefHasUserValue("warnAboutClosingTabs.timeout"))
tabxPrefs.clearUserPref("warnAboutClosingTabs.timeout")
}
else if (this.enableManager && "tabmixdata" in window) {
path = window.tabmixdata.path;
caller = window.tabmixdata.caller;
if (caller == "concatenatewindows")
this.loadSession(path, caller, false);
else
this.loadOneWindow(path, "windowopenebytabmix");
}
// sync rdf list with sessionstore closed tab after restart
// we need it when we delete/restore close tab
// we need this in case that more then one window where opened before restart
else if (this.enableManager && this.enableBackup && this.saveClosedtabs && TMP_ClosedTabs.count > 0) {
this.initSession(gSessionPath[0], gThisWin);
this.copyClosedTabsToRDF(gThisWin);
}
// initialize closed window list broadcaster
if (this.enableManager)
document.getElementById("tmp_closedwindows").setAttribute("disabled",this.isClosedWindowsEmpty());
this.saveStateDelayed();
},
// we call this function after session restored by sessionStore, after restart or after exit private-browsing
onSessionRestored: function SM_onSessionRestored() {
// sync rdf list with sessionstore closed tab after restart
// we need it when we delete/restore close tab
if (this.enableBackup && this.saveClosedtabs && TMP_ClosedTabs.count > 0) {
this.initSession(gSessionPath[0], gThisWin);
this.copyClosedTabsToRDF(gThisWin);
}
// we keep the old session after restart.
// just remove the restore session from close window list
var sessionContainer = this.initContainer(gSessionPath[0]);
this.deleteWithProp(sessionContainer, "status", "saved");
// all the windows that opened by restart will save again with new windowID
// mark all current data with dontLoad flag
var rdfNodeThisWin = this.RDFService.GetResource(gThisWin);
var windowEnum = sessionContainer.GetElements();
while (windowEnum.hasMoreElements()) {
var rdfNodeWindow = windowEnum.getNext();
// skip this window....
if (rdfNodeThisWin == rdfNodeWindow)
continue;
this.setLiteral(rdfNodeWindow, "dontLoad", "true");
}
},
// call by TMP_eventListener.onWindowClose and SessionManager.canQuitApplication, see tablib.js for more
deinit: function SM_deinit(aLastWindow, askBeforSave, aPopUp) {
// When Exit Firefox:
// pref "extensions.tabmix.sessions.onClose"
// 0 - Save
// 1 - Ask me before Save
// 2 (or else) - Don't Save
// we check this when last window is about to close for all other window the session is saved
// in closed window list.
// in the last window if the user pref in not to save we delete the closed window list.
var resultData = {canClose: true, showMorePrompt: true, saveSession: true, removeClosedTabs: false};
if (this.windowClosed || this._inPrivateBrowsing)
return resultData;
// we set aPopUp only in canQuitApplication
if (aPopUp == null)
aPopUp = TMP_checkForPopup(window);
this.lastSaveTabsCount = this.saveOnWindowClose();
if (!aLastWindow) {
if (!aPopUp) {
var thisWinSaveTime = this.getLiteralValue(gThisWin, "timestamp", 0);
this.setLiteral(gSessionPath[0], "timestamp", thisWinSaveTime);
}
return resultData;
}
// we are on the last window........
// we call TMP_Sanitizer.tryToSanitize from TM_deinit
// we don't need to show warnBeforSaveSession dialog if we sanitize TMP without prompet on exit
if (gTabmixPrefs.getBoolPref("privacy.sanitize.sanitizeOnShutdown") && TMP_Sanitizer.isSanitizeTMPwithoutPrompet(true))
return resultData;
if ( this.enableManager ) {
var result = {button: SessionPref.getIntPref("onClose"), checked: this.saveClosedtabs};
if (result.button == 1 && !askBeforSave)
result.button = BUTTON_OK;
var sessionNotEmpty = this.updateClosedWindowList(aPopUp);
if (sessionNotEmpty && result.button == 1) { // Ask me before Save
// result: 0 - save; 1 - cancel quit; 2 - don't save
result = this.warnBeforSaveSession();
resultData.showMorePrompt = false;
if (result.button == BUTTON_CANCEL) {
resultData.canClose = false;
return resultData;
}
}
resultData.saveSession = result.button == BUTTON_OK;
resultData.removeClosedTabs = this.saveClosedTabs && !result.checked;
}
return resultData;
},
saveOnWindowClose: function SM_saveOnWindowClose() {
if (this.enableManager && this.saveThisWindow) {
for (var i = 0; i < gBrowser.mTabs.length; i++)
gBrowser.mTabs[i].removeAttribute("inrestore");
return this.saveOneWindow(gSessionPath[0], "windowclosed");
}
return 0;
},
warnBeforSaveSession: function SM_warnBeforSaveSession() {
window.focus();
var bundle_session = document.getElementById("bundle_session_manager");
var title = bundle_session.getString("sm.askBeforSave.title");
var msg = bundle_session.getString("sm.askBeforSave.msg0") + "\n\n"
+ bundle_session.getString("sm.askBeforSave.msg1");
var chkBoxLabel = bundle_session.getString("sm.saveClosedTab.chkbox.label");
var chkBoxState = this.saveClosedTabs ? CHECKBOX_CHECKED : HIDE_CHECKBOX;
var stringBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle("chrome://global/locale/commonDialogs.properties");
var buttons = this.setLabel("sm.askBeforSave.button0")
+ "\n" + stringBundle.GetStringFromName("Cancel")
+ "\n" + this.setLabel("sm.askBeforSave.button1");
return TM_PromptService([BUTTON_OK, HIDE_MENUANDTEXT, chkBoxState],
[title, msg, "", chkBoxLabel, buttons]);
},
windowIsClosing: function SM_WindowIsClosing(aCanClose, aLastWindow, aSaveSession, aRemoveClosedTabs) {
if (this.windowClosed || this._inPrivateBrowsing)
return;
this.windowClosed = aCanClose;
var _flush = false;
// remove the "dontLoad" flag from current window if user cancel the close
if (this.enableManager && !aCanClose && this.removeAttribute(gThisWin, "dontLoad"))
_flush = true;
// update status flag for this window
// when enableManager is off lastSaveTabsCount is undefined
if (!aLastWindow && this.lastSaveTabsCount && aCanClose) {
this.setLiteral(gThisWin, "status", "saved");
this.updateClosedWindowsMenu(false);
_flush = true;
}
if (aLastWindow && aCanClose) {
if (this.enableManager) {
if (aSaveSession) {
var rdfNodeClosedWindows = this.RDFService.GetResource(gSessionPath[0]);
var sessionContainer = this.initContainer(rdfNodeClosedWindows);
this.deleteWithProp(sessionContainer, "dontLoad");
var count = this.countWinsAndTabs(sessionContainer);
this.setLiteral(rdfNodeClosedWindows, "nameExt", this.getNameData(count.win, count.tab));
// delete closed tab list for this session
if (aRemoveClosedTabs)
this.deleteAllClosedtabs(sessionContainer);
}
else // delete ALL closed window list.
this.deleteSubtree(gSessionPath[0]);
}
// clean-up....
if (this.enableBackup) this.deleteSession(gSessionPath[3]);
if (tabxPrefs.prefHasUserValue("warnAboutClosingTabs.timeout"))
tabxPrefs.clearUserPref("warnAboutClosingTabs.timeout");
if (SessionPref.prefHasUserValue("crashed"))
SessionPref.clearUserPref("crashed"); // we use this in setup.js;
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
this.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "stopped");
if (!this.enableManager && !this.enableBackup)
this.deleteSession(gSessionPath[0]);
_flush = true;
}
if (_flush || this._saveTimer) {
// Make sure to break session manager cycle with the save timer
if (this._saveTimer) {
this._saveTimer.cancel();
this._saveTimer = null;
}
this.saveState();
}
},
// For Firefox 3.0+ call from goQuitApplication when user apply File > Exit
// or when extensions (Mr Tech Toolkit) call goQuitApplication.
canQuitApplication: function SM_canQuitApplication(aBackup) {
// some extension can call goQuitApplication 2nd time (like toolKit)
// we make sure not to run this more the one time
if (this.windowClosed || this._inPrivateBrowsing)
return true;
/*
1. save all window
2. call deinit to the current window (if exist ??)
3 if user don't cancel the quit mark all windows as closed
4. return: true if its ok to close
false if user cancel quit
*/
this.saveAllWindows(gSessionPath[0], "windowclosed", true);
// cheack if all open windows are popup
var allPopups = TMP_checkForPopup(window);
var wnd, enumerator;
enumerator = windowEnumerator();
while ( allPopups && enumerator.hasMoreElements() ) {
wnd = enumerator.getNext();
allPopups = TMP_checkForPopup(wnd);
}
var result = this.deinit(true, !aBackup, allPopups); // we fake that we are the last window
this.windowIsClosing(result.canClose, true, result.saveSession, result.removeClosedTabs);
if (result.canClose) {
enumerator = windowEnumerator();
while ( enumerator.hasMoreElements() ) {
wnd = enumerator.getNext();
wnd.SessionManager.windowClosed = true;
}
}
return result.canClose;
},
// XXX split this for each pref that has change
// XXX need to update after permissions, locked......
updateSettings: function SM_updateSettings() {
// list of session manager pref
// sessions.manager - ok
// sessions.crashRecovery - ok
// sessions.save.closedtabs - ok
// sessions.save.history - ok
// sessions.save.permissions - ok (update evry time this function run because lock is change)
// sessions.save.locked - ok (update evry time this function run because lock is change)
// sessions.save.protected - ok (update evry time this function run because lock is change)
// sessions.save.selectedtab - ok
// xxx sessions.save.scrollposition - ok (update with history) // xxx need to divide it
// undoClose -
// browser.sessionstore.max_tabs_undo
//
var sessionManager = tabxPrefs.getBoolPref("sessions.manager") && !this._inPrivateBrowsing;
var crashRecovery = tabxPrefs.getBoolPref("sessions.crashRecovery") && !this._inPrivateBrowsing;
var enableClosedtabs = tabxPrefs.getBoolPref("sessions.save.closedtabs");
var enableSaveHistory = tabxPrefs.getBoolPref("sessions.save.history");
var undoClose = tabxPrefs.getBoolPref("undoClose");
var maxTabsUndo = gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
// hide or show session manager buttons & menus
document.getElementById("tm-sessionmanager").hidden = !sessionManager || !tabxPrefs.getBoolPref("sessionToolsMenu");
var hiddenPref = !sessionManager || !tabxPrefs.getBoolPref("closedWinToolsMenu");
document.getElementById("tm-sm-closedwindows").hidden = hiddenPref;
document.getElementById("tm-sm-closedwindows2").hidden = !hiddenPref;
TMP_setItem("tmp_sessionmanagerButton", "disabled", !sessionManager || null);
// we dont need this function to run before sessionmanager init
// this.enableBackup=null so we dont past the next if
if (!this.DATASource) return;
var windowSaved = false, closedTabSaved = false;
if (this.enableBackup != crashRecovery) {
if (crashRecovery) { // save all open window and tab
this.saveAllWindows(gSessionPath[0], "windowbackup");
windowSaved = true;
} else { // remove all backup
this.deleteSession(gSessionPath[0], "status", "backup");
this.deleteSession(gSessionPath[3]);
this.initSession(gSessionPath[0], gThisWin);
this.saveStateDelayed();
}
this.enableBackup = crashRecovery;
}
var winPath = gThisWin;
if (crashRecovery)
this.tabSelected(); // this is fast so we dont check if the pref is changed ( just for now)
if (this.enableManager != sessionManager) {
this.enableManager = sessionManager;
}
// changing in browser.sessionstore.max_tabs_undo or undoClose pref maintained in TMP_PrefObserver observe
if (this.saveClosedtabs != enableClosedtabs && undoClose) {
this.saveClosedtabs = enableClosedtabs && undoClose;
if (enableClosedtabs) {
// save if gBrowser.closedTabs.length > 0 and we save backup and save closedtab backup
if (crashRecovery && TMP_ClosedTabs.count > 0 && undoClose) {
this.initSession(gSessionPath[0], winPath);
this.copyClosedTabsToRDF(winPath);
}
closedTabSaved = true;
} else {
// if undoClose = false we delete all in TMP_PrefObserver observe
if (undoClose) this.deleteWinClosedtabs(winPath); // flush only closedTabs list in session.RDF
}
}
if (this.enableSaveHistory != enableSaveHistory) {
this.enableSaveHistory = enableSaveHistory;
if (crashRecovery) {
if (!windowSaved) this.saveAllTab(winPath, 0);
if (!closedTabSaved && enableClosedtabs && TMP_ClosedTabs.count > 0 && undoClose) {
this.initSession(gSessionPath[0], winPath);
this.deleteWinClosedtabs(winPath);
this.copyClosedTabsToRDF(winPath);
closedTabSaved = true;
}
}
}
if (closedTabSaved) {
this.initSession(gSessionPath[0], gThisWin);
this.saveStateDelayed();
}
},
loadHomePage: function SM_loadHomePage() {
var homePage = gHomeButton.getHomePage();
if ("arguments" in window && homePage == window.arguments[0]) {
this.setStripVisibility(homePage.split("|").length);
BrowserHome();
if (!gBrowser.isBlankBrowser(gBrowser.selectedBrowser))
content.focus();
}
else if (gBrowser.mCurrentTab.loadOnStartup)
for (var i = 0; i < gBrowser.mTabs.length ; i++)
delete gBrowser.mTabs[i].loadOnStartup;
else
gBrowser.selectedBrowser.reload();
},
// init common services
initService: function() {
this.RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
this.CONUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]
.getService(Components.interfaces.nsIRDFContainerUtils);
this.setNC_TM();
this.initDATASource();
},
initDATASource: function SM_initDATASource() {
var file = this.profileDir;
file.append("session.rdf");
var uri = gIOService.newFileURI(file).spec;
try {
this.DATASource = this.RDFService.GetDataSourceBlocking(uri);
} catch (e) { // corrupted session.rdf
var bundle_session = document.getElementById("bundle_session_manager");
var title = bundle_session.getString("sm.corrupted.title");
var msg = bundle_session.getString("sm.corrupted.msg0") + "\n"
+ bundle_session.getString("sm.corrupted.msg1");
var buttons = ["", this.setLabel("sm.button.continue")].join("\n");
TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],[title, msg, "", "", buttons]);
TMP_ASSERT(e);
file.moveTo(this.profileDir, "session.old");
this.DATASource = this.RDFService.GetDataSourceBlocking(uri);
this.corruptedFile = true;
}
// set path to session type
var path = gRDFRoot + "/closedSession/";
var sessionType = ["thisSession", "lastSession", "previoustolastSession", "crashedsession"];
var closedSession = this.initContainer(path);
var i, aEntry;
if (closedSession.GetCount()==0) { // create the list
for (i = 0; i < sessionType.length; i++) {
aEntry = this.RDFService.GetResource(path + sessionType[i]);
this.setResource(aEntry, "session", gRDFRoot + "/closed" + i + "/window");
closedSession.AppendElement(aEntry);
}
}
for (i = 0; i < sessionType.length; i++) {
gSessionPath[i] = this.getResourceValue(path + sessionType[i], "session");
}
if (typeof(gBrowser) == "object" && !gBrowser.windowID) {
gBrowser.windowID = this.getAnonymousId();
gThisWin = gSessionPath[0] + "/" + gBrowser.windowID;
gThisWinTabs = gThisWin + "/tabs";
gThisWinClosedtabs = gThisWin + "/closedtabs";
}
},
get profileDir() {
return Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsILocalFile);
},
getAnonymousId: function() {
const kSaltTable = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0' ];
var id = "";
for (var i = 0; i < 8; ++i) {
id += kSaltTable[Math.floor(Math.random() * kSaltTable.length)];
}
return id;
},
setNC_TM: function() {
var rdfLabels = ["tabs","closedtabs","index","history","properties","selectedIndex",
"timestamp","title","url","dontLoad","reOpened","name","nameExt","session",
"status","tabPos","image","scroll","winFeatures"];
for (var i = 0; i < rdfLabels.length; i++) {
NC_TM[rdfLabels[i]] = this.RDFService.GetResource(this.NC_NS + rdfLabels[i]);
}
},
setLabel: function(property, bundleID) {
var strimgBundle = bundleID ? document.getElementById(bundleID) :
document.getElementById("bundle_session_manager");
var label = strimgBundle.getString(property + ".label");
var key = strimgBundle.getString(property + ".accesskey");
var accessKeyIndex = label.toLowerCase().indexOf(key.toLowerCase());
if (accessKeyIndex > -1)
label = label.substr(0, accessKeyIndex) + "&" + label.substr(accessKeyIndex);
return label;
},
deleteNode: function(rdfNode) {
var arcOut = this.DATASource.ArcLabelsOut(rdfNode);
while (arcOut.hasMoreElements()) {
var aLabel = arcOut.getNext();
if (aLabel instanceof tmRDFResource) {
var aTarget = this.DATASource.GetTarget(rdfNode, aLabel, true);
this.DATASource.Unassert(rdfNode, aLabel, aTarget);
}
}
},
deleteSubtree: function(labelRoot) {
var allElements = this.DATASource.GetAllResources();
while (allElements.hasMoreElements()) {
var aResource = allElements.getNext();
if ((aResource instanceof tmRDFResource) && (aResource.Value.indexOf(labelRoot) == 0))
this.deleteNode(aResource);
}
},
initContainer: function(node) {
var pNode = node;
try{
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
return this.CONUtils.MakeSeq(this.DATASource, node);
} catch (e) {
TMP_ASSERT(e);
return "error"
}
},
// return true if node is empty container or node is not container
containerEmpty: function(node) {
var pNode = node;
try{
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
if (!this.CONUtils.IsContainer(this.DATASource, node)) return true;
return this.CONUtils.IsEmpty(this.DATASource, node);
} catch (e) {
TMP_ASSERT(e);
return "error"
}
},
wrapContainer: function SM_wrapContainer(path, prop) {
var root = this.getResource(path, prop);
var container = this.initContainer(root);
if (container == "error") { tmLog("wrapContainer error path " + path + "\n" + "prop " + prop); return "error"}
return {
Root: root,
Container: container,
Enum: container.GetElements(),
Count: container.GetCount()
}
},
getValue: function(node, label, typeID, def) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
label = NC_TM[label];
var rdfNode = this.DATASource.GetTarget(node, label, true);
return (rdfNode instanceof Components.interfaces[typeID]) ? rdfNode.Value : def;
},
getLiteralValue: function(node, arc, def) {
if (typeof(def) == "undefined") def = "";
return this.getValue(node, arc, "nsIRDFLiteral", def);
},
getIntValue: function(node, arc, def) {
if (typeof(def) == "undefined") def = 0;
return this.getValue(node, arc, "nsIRDFInt", def);
},
getResourceValue: function(node, arc, def) {
if (typeof(def) == "undefined") def = null;
return this.getValue(node, arc, "nsIRDFResource", def);
},
getResource: function(node, arc) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
arc = NC_TM[arc];
return this.DATASource.GetTarget(node, arc, true);
},
nodeHasArc: function(node, arc) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
arc = NC_TM[arc];
return this.DATASource.hasArcOut(node, arc);
},
setLiteral: function SM_setLiteral(node, arc, value) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
arc = NC_TM[arc];
value = this.RDFService.GetLiteral(value);
this.changeValue(node, arc, value);
},
setIntLiteral: function(node, arc, value) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
arc = NC_TM[arc];
value = this.RDFService.GetIntLiteral(value);
this.changeValue(node, arc, value);
},
setResource: function(node, arc, value) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
arc = NC_TM[arc];
if (typeof(value) == "string") value = this.RDFService.GetResource(value);
this.changeValue(node, arc, value);
},
changeValue: function(node, arc, newValue) {
if (this.DATASource.hasArcOut(node, arc)) {
var oldValue = this.DATASource.GetTarget(node, arc, true);
if (newValue != oldValue) this.DATASource.Change(node, arc, oldValue, newValue);
} else this.DATASource.Assert(node, arc, newValue, true);
},
// use it only to remove node with literal value
removeAttribute: function(node, arc) {
if (typeof(node) == "string") node = this.RDFService.GetResource(node);
if (this.nodeHasArc(node, arc)) {
var value = this.getLiteralValue(node, arc);
this.DATASource.Unassert(node, NC_TM[arc], this.RDFService.GetLiteral(value));
return true;
}
return false; // arc not found
},
_saveTimer: null,
// time in milliseconds (Date.now()) when the session was last written to file
_lastSaveTime: 0,
// minimal interval between two save operations (in milliseconds)
_interval: 2000, // 10000
saveStateDelayed: function SM_saveStateDelayed(aDelay) {
if (!this._saveTimer) {
// interval until the next disk operation is allowed
var minimalDelay = this._lastSaveTime + this._interval - Date.now();
// if we have to wait, set a timer, otherwise saveState directly
aDelay = Math.max(minimalDelay, aDelay || 2000);
if (aDelay > 0) {
this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
}
else {
this.saveState();
}
}
},
QueryInterface: function(aIID){
if (aIID.equals(Components.interfaces.nsIObserver) ||
aIID.equals(Components.interfaces.nsISupports) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference))
return this;
throw Components.results.NS_NOINTERFACE;
},
closeProtectedTabs: function(){
var protectedTabs = gBrowser.mTabContainer.getElementsByAttribute("protected", true);
for (var i = protectedTabs.length - 1 ; i >= 0; i--) {
var tab = protectedTabs[i];
tab.removeAttribute("protected");
gBrowser.removeTab(tab);
}
},
observe: function SM_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "quit-application-requested":
case "browser-lastwindow-close-requested":
this.browserStartupPref = gTabmixPrefs.getIntPref("browser.startup.page");
break;
case "timer-callback": // timer call back for delayed saving
this._saveTimer = null;
this.saveState();
break;
case "browser-window-change-state":
this.toggleRecentlyClosedWindowsButton();
break;
case "private-browsing":
switch (aData) {
case "enter":
// check if we need to close protected tab here
var needToCloseProtected = true;
try {
if (gTabmixPrefs.getBoolPref("browser.privatebrowsing.keep_current_session"))
needToCloseProtected = false;
} catch (ex) {}
// noting to do here if we are not using tabmix session manager
if (!SessionPref.getBoolPref("manager") && !SessionPref.getBoolPref("crashRecovery")) {
// nsPrivateBrowsingService.js can not close protected tab we have to do it our self
// we only close this tab here after nsPrivateBrowsingService save the session
if (needToCloseProtected)
this.closeProtectedTabs();
this._inPrivateBrowsing = true;
TMP_ClosedTabs.setButtonDisableState(true);
this.toggleRecentlyClosedWindowsButton();
break;
}
// save curent state
this.canQuitApplication(true);
this._inPrivateBrowsing = true;
if (needToCloseProtected)
this.closeProtectedTabs();
this.enableManager = SessionPref.getBoolPref("manager") && !this._inPrivateBrowsing;
this.enableBackup = SessionPref.getBoolPref("crashRecovery") && !this._inPrivateBrowsing;
this.updateSettings();
TMP_ClosedTabs.setButtonDisableState(true);
break;
case "exit":
// nsPrivateBrowsingService.js can not close protected tab we have to do it our self
this.closeProtectedTabs();
aSubject.QueryInterface(Ci.nsISupportsPRBool);
var quitting = aSubject.data;
if (quitting)
break;
// build-in sessionStore restore the session for us
if (!SessionPref.getBoolPref("manager") && !SessionPref.getBoolPref("crashRecovery")) {
this._inPrivateBrowsing = false;
var self = this;
window.setTimeout(function () {
TMP_ClosedTabs.setButtonDisableState();
self.toggleRecentlyClosedWindowsButton();
}, 100);
break;
}
TMP_ClosedTabs.getClosedTabAtIndex(-1); // to be on the safe side...
window.setTimeout(function () {TMP_ClosedTabs.setButtonDisableState(); },gIsFirefox36 ? 0 : 100);
this.afterExitPrivateBrowsing = window.setTimeout(function (self) {
self._inPrivateBrowsing = false;
self.windowClosed = false;
self.onSessionRestored();
self.updateSettings();
self.removeAttribute(gThisWin, "dontLoad");
self.saveStateDelayed();
self.afterExitPrivateBrowsing = null;
},0, this);
break;
}
break;
}
},
restoreWindow: function SM_restoreWindow(aWhere, aIndex) {
switch (aWhere) {
case "delete":
this.forgetClosedWindow(aIndex);
break;
case "window":
default:
undoCloseWindow(aIndex);
this.notifyClosedWindowsChanged();
}
},
/**
* @brief catch middle click from closed windows list,
* delete window from the list or resrore acurding to the pref
* @param aEvent a valid event union.
* @returns noting.
*
* we can use ss.forgetClosedWindow(index) from Firefox 3.7a1pre after 2009-08-14
*
*/
checkForMiddleClick: function SM_checkForMiddleClick(aEvent) {
if (aEvent.button != 1)
return;
aEvent.stopPropagation();
var index = "value" in aEvent.originalTarget ? aEvent.originalTarget.value : -1;
if (index < 0)
return;
var where = tabxPrefs.getBoolPref("middleclickDelete") ? 'delete' : 'tab';
SessionManager.restoreWindow(where, index);
SessionManager.notifyClosedWindowsChanged();
var popup = aEvent.originalTarget.parentNode;
if (TMP_ClosedTabs.ss.getClosedWindowCount() > 0)
HistoryMenu.populateUndoWindowSubmenu(popup.id);
else {
popup.hidePopup();
if (popup.parentNode.id != "btn_closedwindows")
popup.parentNode.parentNode.hidePopup();
}
},
forgetClosedWindow: function SM_forgetClosedWindow(aIndex) {
// from firefox 3.6+
if ("forgetClosedWindow" in TMP_ClosedTabs.ss) {
if (aIndex < 0) {
while (TMP_ClosedTabs.ss.getClosedWindowCount() > 0) {
TMP_ClosedTabs.ss.forgetClosedWindow(0);
}
}
else
TMP_ClosedTabs.ss.forgetClosedWindow(aIndex);
}
else
this.getClosedWindowAtIndex(aIndex);
this.notifyClosedWindowsChanged();
},
/**
* @brief fetch the data of closed window, while removing it from the array
* @param aIndex a Integer value - 0 or grater index to remove
* other value empty the list.
* @returns closed window data at aIndex.
*
* we can use ss.forgetClosedWindow(index) from Firefox 3.6a2pre after 2009-08-14
*
*/
getClosedWindowAtIndex: function SM_getClosedWindowAtIndex(aIndex) {
var closedWindow
var state = { windows: [{tabs:[]}], _firstTabs: false , _closedWindows:[]};
// if aIndex is not > 0 we just past empy list to setWindowState
// it's like remove all closed tabs from the list
if (aIndex >= 0) {
state._closedWindows = eval("(" + TMP_ClosedTabs.ss.getClosedWindowData() + ")");
// purge closed window at aIndex
closedWindow = state._closedWindows.splice(aIndex, 1).shift();
}
// replace existing _closedWindows
try {
TMP_ClosedTabs.ss.setWindowState(window, state.toSource(), false);
} catch (ex) {TMP_ASSERT(ex);}
return closedWindow;
},
notifyClosedWindowsChanged: function SM_notifyClosedWindowsChanged() {
var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
observerService.notifyObservers(null, "browser-window-change-state", "changed");
},
// enable/disable the Recently Closed Windows button
toggleRecentlyClosedWindowsButton: function SM_toggleRecentlyClosedWindowsButton() {
var button = document.getElementById("btn_closedwindows");
if (button && !this.enableManager) {
// no restorable windows, so disable menu
if (HistoryMenu._ss.getClosedWindowCount() == 0)
button.setAttribute("disabled", true);
else
button.removeAttribute("disabled");
}
},
saveState: function SM_saveState() {
// if we're in private browsing mode, do nothing
if (this._inPrivateBrowsing)
return;
try {
this.DATASource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
this._lastSaveTime = Date.now();
}
catch (ex) {
TMP_ASSERT(ex, "Error when tabmix try to write to session.rdf file");
if (this._interval < 10000) {
this._interval += 500;
this.saveStateDelayed();
}
}
},
promptReplaceStartup: function(caller, path) {
var loadsession = SessionPref.getIntPref("onStart.loadsession");
var sessionpath = SessionPref.getCharPref("onStart.sessionpath");
var result = {button: NO_NEED_TO_REPLACE};
if (loadsession < 0 || sessionpath != path) return result;
var label = this.getLiteralValue(path, "name");
var selectionFlag = SELECT_DEFAULT;
var title, msg, buttons;
var bundle_session = document.getElementById("bundle_session_manager");
var areYouSure = bundle_session.getString("sm.areYouSure.msg");
var chooseStartup = bundle_session.getString("sm.canChooseStartup.msg");
switch ( caller ) {
case "addWinToSession":
title = bundle_session.getString("sm.addtoStartup.title");
var msgType = caller=="addWinToSession" ? "windows" : "tabs";
msg = bundle_session.getString("sm.addtoStartup.msg." + msgType) + "\n" + label
+ "\n" + areYouSure + "\n\n" + chooseStartup;
buttons = [this.setLabel("sm.addtoStartup.button0"),
this.setLabel("sm.addtoStartup.button1")].join("\n");
break;
case "replaceSession":
title = bundle_session.getString("sm.replaceStartup.title");
msg = bundle_session.getString("sm.replaceStartup.msg") + "\n" + label
+ "\n" + areYouSure + "\n\n" + chooseStartup;
buttons = [this.setLabel("sm.replaceStartup.button0"),
this.setLabel("sm.replaceStartup.button1")].join("\n");
break;
case "removeSavedSession":
title = bundle_session.getString("sm.removeStartup.title");
msg = bundle_session.getString("sm.removeStartup.msg0") + "\n" + label
+ "\n" + areYouSure + "\n\n" + bundle_session.getString("sm.removeStartup.msg1");
buttons = [this.setLabel("sm.removeStartup.button0"),
this.setLabel("sm.removeStartup.button1")].join("\n");
selectionFlag = SELECT_LASTSESSION;
break;
}
return TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, selectionFlag],
[title, msg, "", "", buttons]);
},
addWinToSession: function SM_addWinToSession(action) {
if (!this.isValidtoSave()) return;
var path = document.popupNode.session;
var result = this.promptReplaceStartup("addWinToSession", path);
if (result.button == BUTTON_CANCEL) return;
else if (result.button == BUTTON_OK) this.replaceStartupPref(result, "");
var saveClosedTabs = this.saveClosedtabs;
var rdfNodeSession = this.RDFService.GetResource(path);
var sessionContainer = this.initContainer(rdfNodeSession);
var oldCount = this.countWinsAndTabs(sessionContainer);
var newCount = this.saveOneOrAll(action, path, saveClosedTabs);
if (newCount) {
var numTabs = oldCount.tab + newCount.tab;
var numWindows = oldCount.win + newCount.win;
this.setLiteral(rdfNodeSession, "nameExt", this.getNameData(numWindows, numTabs));
this.saveStateDelayed();
}
},
saveClosedSession: function SM_saveClosedSession() {
var oldPath = document.popupNode.session;
var id = this.getAnonymousId();
var path = gRDFRoot + "/saved/" + id + "/window";
var pathToReplace = "";
var session = this.getSessionName("saveprevious", this.getLiteralValue(oldPath, "name"));
if (session.button == BUTTON_CANCEL) return; // user cancel
else if (session.button == BUTTON_EXTRA1) { // we replace exist session, BUTTON_OK - save new session
var result = this.promptReplaceStartup("replaceSession", session.path);
if (result.button == BUTTON_CANCEL) return; // user cancel
else if (result.button == BUTTON_OK) { // we replace startup session
this.replaceStartupPref(result, path);
}
pathToReplace = session.path;
}
container = this.initContainer(path)
var pathNode, container, extID = "";
var node = document.popupNode.parentNode.parentNode;
if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows")
extID = "/" + id;
this.copySubtree(oldPath, path + extID);
if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
node = this.RDFService.GetResource(path + extID);
container.InsertElementAt(node, 1, true);
this.DATASource.Unassert(node, NC_TM["dontLoad"], this.RDFService.GetLiteral("true"));
}
var count = this.countWinsAndTabs(container); // we need it just to fix the date
if (!session.saveClosedTabs)
this.deleteAllClosedtabs(container);
if (count)
this.insertSession(count, session.name, path, pathToReplace);
else
tmLog("Error in saveClosedSession");
},
copyNode: function(oldNode, newNode, oldRoot, newRoot) {
var newTarget;
var arcOut = this.DATASource.ArcLabelsOut(oldNode);
while (arcOut.hasMoreElements()) {
var aLabel = arcOut.getNext();
if (aLabel instanceof tmRDFResource) {
var aTarget = this.DATASource.GetTarget(oldNode, aLabel, true);
if (aTarget instanceof tmRDFResource) {
newTarget = aTarget.Value.replace(oldRoot, newRoot);
aTarget = this.RDFService.GetResource(newTarget);
}
this.DATASource.Assert(newNode, aLabel, aTarget, true);
}
}
},
copySubtree: function (oldRoot, newRoot) {
var allElements = this.DATASource.GetAllResources();
while (allElements.hasMoreElements()) {
var aResource = allElements.getNext();
if ((aResource instanceof tmRDFResource) && (aResource.Value.indexOf(oldRoot) == 0)) {
var newNodeLabel = aResource.Value.replace(oldRoot, newRoot);
this.copyNode(aResource, this.RDFService.GetResource(newNodeLabel), oldRoot, newRoot);
}
}
},
replaceStartupPref: function(result, newPath) {
var sessionpath = !newPath ? "--" : SessionPref.getCharPref("onStart.sessionpath");
SessionPref.setIntPref("onStart.loadsession", result.value);
if (result.value > -1) {
if (result.label == sessionpath ) SessionPref.setCharPref("onStart.sessionpath", newPath);
else SessionPref.setCharPref("onStart.sessionpath", result.label);
}
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
},
sessionUtil: function(action, what) {
// action = save , replace
// type = thiswindow , allwindows
if (!this.isValidtoSave()) return;
if (numberOfWindows() == 1) what = "thiswindow";
var oldPath = "", name, saveClosedTabs;
var id = this.getAnonymousId();
var newPath = gRDFRoot + "/saved/" + id + "/window";
if (action == "save") {
// ask the user for new name or for exist name if the user want to replace
var session = this.getSessionName("save"+what);
if (session.button == BUTTON_CANCEL) return; // user cancel
else if (session.button == BUTTON_EXTRA1) oldPath = session.path;
name = session.name;
saveClosedTabs = session.saveClosedTabs;
} else {
oldPath = document.popupNode.session;
name = this.getLiteralValue(oldPath, "name");
saveClosedTabs = this.saveClosedtabs;
}
if (oldPath != "") { // oldPath is "" if we save to a new name
// check if the user want to replace startup session
var result = this.promptReplaceStartup("replaceSession", oldPath);
if (result.button == BUTTON_CANCEL) return; // user cancel
else if (result.button == BUTTON_OK) this.replaceStartupPref(result, newPath);
}
var count = this.saveOneOrAll("save"+what, newPath, saveClosedTabs);
if (count) this.insertSession(count, name, newPath, oldPath);
else tmLog("Error in " + action + " " + what);
},
isValidtoSave: function() {
if ( !this.enableManager ) return false;
var bundle_session = document.getElementById("bundle_session_manager");
if (gBrowser.isBlankWindow()) {
var title = bundle_session.getString("sm.title");
var msg = bundle_session.getString("sm.dontSaveBlank.msg");
var buttons = ["", this.setLabel("sm.button.continue")].join("\n");
TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],[title, msg, "", "", buttons]);
return false;
}
return true;
},
saveOneOrAll: function(action, path, saveClosedTabs) {
var numTabs, numWindows;
switch ( action ) {
case "savethiswindow":
numTabs = this.saveOneWindow(path, "", false, saveClosedTabs);
numWindows = 1;
break;
case "saveallwindows":
var didSaved = this.saveAllWindows(path, "", saveClosedTabs);
numTabs = didSaved.tab;
numWindows = didSaved.win;
break;
default: return false;
}
if (numTabs > 0) return {win: numWindows, tab: numTabs};
bundle_session = document.getElementById("bundle_session_manager");
alert(bundle_session.getString("sm.sessoinSave.error"));
return false;
},
insertSession: function SM_insertSession(count, name, path, oldPath) {
var container = this.initContainer(gRDFRoot + "/windows");
var index = 0;
if (oldPath != "") index = container.IndexOf(this.RDFService.GetResource(oldPath));
var node = this.RDFService.GetResource(path);
container.InsertElementAt(node, index+1, true);
if (oldPath != "") { // remove the session we replace
container.RemoveElementAt(index, true);
this.removeSession(oldPath, gRDFRoot+'/windows');
}
this.setLiteral(node, "name", name);
this.setLiteral(node, "nameExt", this.getNameData(count.win, count.tab));
this.saveStateDelayed();
return true;
},
getSessionName: function(action, old) {
var bundle_session = document.getElementById("bundle_session_manager");
var showChebox, closedtabMsg, saveClosedTabs = this.saveClosedtabs;
if (action != "rename" && saveClosedTabs) {
closedtabMsg = bundle_session.getString("sm.saveClosedTab.chkbox.label");
showChebox = CHECKBOX_CHECKED;
} else showChebox = HIDE_CHECKBOX;
var msg = bundle_session.getString("sm.sessionName.msg0") + "\n";
var title = bundle_session.getString("sm.sessionName.title." + action);
var label, buttons, actionFlag;
var sessionList = this.getSessionList("saved");
if (action=="rename") {
label = old;
buttons = [this.setLabel("sm.sessionName.button0"),
this.setLabel("sm.sessionName.button1")].join("\n");
actionFlag = DLG_RENAME;
} else {
label = action == "saveprevious" ? old : gBrowser.mCurrentTab.label;
buttons = [this.setLabel("sm.askBeforSave.button0"),
this.setLabel("sm.askBeforSave.button1"),
this.setLabel("sm.replaceStartup.button0")+"..."].join("\n");
actionFlag = DLG_SAVE;
for (var i = 0; i < sessionList.list.length; i++) {
if (label == sessionList.list[i]) {
label = "";
break;
}
}
}
label = unescape(label + "\n" + sessionList.list.join("\n"));
var result = TM_PromptService([BUTTON_OK, SHOW_TEXTBOX, showChebox, actionFlag],[title, msg, label, closedtabMsg, buttons]);
switch (result.button) {
case BUTTON_CANCEL: return {button: result.button};
case BUTTON_OK:
case BUTTON_EXTRA1 :
var trimResult = result.label.replace(/^[\s]+/g,"").replace(/[\s]+$/g,"");
return {button: result.button, name: escape(trimResult), path: sessionList.path[result.value], saveClosedTabs: result.checked};
}
return {};
},
countWinsAndTabs: function SM_countWinsAndTabs(container, prop) {
// count windows and tabs in this session
var numTabs = 0, numWindows = 0;
var windowEnum = container.GetElements();
while (windowEnum.hasMoreElements()) {
var rdfNodeWindow = windowEnum.getNext();
if (prop && this.nodeHasArc(rdfNodeWindow, prop))
continue;
numWindows += 1;
var rdfNodeTabs = this.getResource(rdfNodeWindow, "tabs");
if (rdfNodeTabs instanceof tmRDFResource) {
var tabContainer = this.initContainer(rdfNodeTabs);
numTabs += tabContainer.GetCount();
}
}
return {win: numWindows, tab: numTabs};
},
getNameData: function(numWindows, numTabs) {
var d = new Date();
var date = [d.getFullYear(), '/', d.getMonth()<9 ? "0":"", d.getMonth()+1, '/', d.getDate()<10 ? "0":"", d.getDate()].join('');
var time = [d.getHours()<10 ? "0":"", d.getHours(), ':', d.getMinutes()<10 ? "0":"", d.getMinutes(), ':', d.getSeconds()<10 ? "0":"", d.getSeconds()].join('');
var bundle_session = document.getElementById("bundle_session_manager");
var empty = bundle_session.getString("sm.session.empty");
var T = bundle_session.getString("sm.session.tabs");
var W = bundle_session.getString("sm.session.windows");
if (numWindows == 0) return ", (" + empty + ") (" + date + " " + time + ")";
else if (numWindows < 2) return ", (" + numTabs + " "+ T + ") (" + date + " " + time + ")";
return ", (" + numWindows + " " + W + ", " + numTabs + " " + T + ") (" + date + " " + time + ")";
},
updateSessionMenu: function(menu) {
if (typeof(document.popupNode.session) == "undefined")
return false;
var overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows") || gSingleWindowMode;
document.getElementById("tm-sm-OpenInCurrenWindow").setAttribute("default",overwriteWindows);
document.getElementById("tm-sm-OpenInNewWindow").setAttribute("default",!overwriteWindows);
document.getElementById("tm-sm-OpenInNewWindow").hidden = gSingleWindowMode;
var mValue = document.popupNode.getAttribute("value");
if (mValue <= -1)
document.getElementById("tm-sm-Rename").setAttribute("disabled",true);
else
document.getElementById("tm-sm-Rename").removeAttribute("disabled");
var node = document.popupNode.parentNode.parentNode;
var mItem = document.getElementById("tm-sm-SetAsStartup");
if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
mItem.removeAttribute("disabled");
if (document.popupNode.hasAttribute("default"))
mItem.setAttribute("checked", "true");
else
mItem.removeAttribute("checked");
} else {
mItem.removeAttribute("checked");
mItem.setAttribute("disabled",true);
}
var mShowext = document.getElementById("tm-sm-showext");
var showext = SessionPref.getBoolPref("menu.showext");
if (!showext && mShowext.hasAttribute("checked"))
mShowext.removeAttribute("checked");
else if (showext && !mShowext.hasAttribute("checked"))
mShowext.setAttribute("checked", "true");
var obsAll = document.getElementById("tmp_contextmenu_AllWindows");
var obsThis = document.getElementById("tmp_contextmenu_ThisWindow");
var mSave = document.getElementById("tm-sm-Save");
if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows" || mValue <= -1) {
if (obsAll.hidden != true)
obsAll.hidden = true;
if (obsThis.hidden != true)
obsThis.hidden = true;
if (mSave.hidden != false)
mSave.hidden = false;
if (document.popupNode.hasAttribute("disabled"))
mSave.setAttribute("disabled", true);
else
mSave.removeAttribute("disabled");
} else {
var isOneWindow = (numberOfWindows() == 1);
if (obsAll.hidden != isOneWindow)
obsAll.hidden = isOneWindow;
if (obsThis.hidden != false)
obsThis.hidden = false;
if (mSave.hidden != true)
mSave.hidden = true;
}
return true;
},
restoreSession: function(node, overwriteWindows) {
if (!this.enableManager)
return;
// call restoreSession after delay to let the popup menu time to hide
window.setTimeout( function () { SessionManager.delayRestoreSession(node, overwriteWindows); }, 0 );
},
delayRestoreSession: function(node, overwriteWindows) {
var path = node.session;
var id = node.parentNode.parentNode.id;
if (id == "tm-sessionmanager" || id == "btn_sessionmanager")
this.loadSession(path,'sessionrestore', overwriteWindows);
else if (id.indexOf("tm-sm-closedwindows")==0 || id == "btn_closedwindows")
this.openclosedwindow(path, overwriteWindows);
},
setSessionAsStartup: function(popup) {
if (popup.getAttribute("checked")) {
var aValue = document.popupNode.getAttribute("value"); // -1, -2 for for closed session, 1,2.... for saved session
var loadsession = aValue && aValue <= -1 ? aValue : 0;
SessionPref.setIntPref("onStart.loadsession", loadsession);
if (loadsession > -1)
SessionPref.setCharPref("onStart.sessionpath", document.popupNode.session);
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
}
},
setShowNameExt: function() {
SessionPref.setBoolPref("menu.showext", !SessionPref.getBoolPref("menu.showext"));
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
},
renameSession: function SM_renameSession(thisSession) {
var node = this.RDFService.GetResource(thisSession);
var oldName = this.getLiteralValue(node, "name");
var result = this.getSessionName("rename", oldName);
if (result.button == BUTTON_OK) {
this.setLiteral(node, "name", result.name);
this.saveStateDelayed();
}
},
removeFromMenu: function(event, popup, root) {
if (!tabxPrefs.getBoolPref("middleclickDelete")) return;
if ( event.button == 1 && ("session" in event.target)) {
SessionManager.removeSavedSession(event.target);
if (root == gSessionPath[0] && this.isClosedWindowsEmpty()) popup.hidePopup();
else SessionManager.createMenu(popup, root);
}
},
removeSavedSession: function(aMenuItem, aRemoveSession) {
var node = aMenuItem.parentNode.parentNode;
var path = aMenuItem.session;
if (aRemoveSession || node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
// before we remove this session check if it is the startup session
// and let the user cancel the delete or choose diffrent startup session
var result = this.promptReplaceStartup("removeSavedSession", path);
switch (result.button) {
case BUTTON_CANCEL: return;
case BUTTON_OK: this.replaceStartupPref(result, "");
case NO_NEED_TO_REPLACE : this.removeSession(path, gRDFRoot+'/windows');
}
} else if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
this.removeSession(path, gSessionPath[0]);
this.updateClosedWindowsMenu("check");
}
},
removeAllSavedSession: function SM_removeAllSavedSession() {
var node = document.popupNode.parentNode.parentNode;
var result, title, msg;
var bundle_session = document.getElementById("bundle_session_manager");
var buttons = [this.setLabel("sm.removeStartup.button0"),
this.setLabel("sm.removeStartup.button1")].join("\n");
if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
title = bundle_session.getString("sm.removeAll.title.session");
msg = bundle_session.getString("sm.removeAll.msg0") + "\n\n";
if (SessionPref.getIntPref("onStart.loadsession") > -1)
msg += bundle_session.getString("sm.removeAll.msg1");
result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],
[title, msg, "", "", buttons]);
if (result.button == BUTTON_OK) {
this.deleteSubtree(gSessionPath[1]);
this.deleteSubtree(gSessionPath[2]);
this.deleteSubtree(gSessionPath[3]);
this.deleteSubtree(gRDFRoot+'/saved');
this.deleteSubtree(gRDFRoot+'/windows');
this.saveStateDelayed();
SessionPref.setIntPref("onStart.loadsession", -1);
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
}
} else if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
title = bundle_session.getString("sm.removeAll.title.closedwindow");
msg = bundle_session.getString("sm.removeAll.msg2");
result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],
[title, msg, "", "", buttons]);
if (result.button == BUTTON_OK) {
var sessionContainer = this.initContainer(gSessionPath[0]);
this.deleteWithProp(sessionContainer, "status", "saved");
this.updateClosedWindowsMenu(true);
this.saveStateDelayed();
}
}
},
// xxx need to check if we need all this functions
removeSession: function SM_removeSession(value, container) {
if (value==null) return;
var node = this.RDFService.GetResource(value);
var rdfNodeWindows = this.RDFService.GetResource(container);
var windowsContainer = this.initContainer(rdfNodeWindows);
this.deleteSubtree(value);
windowsContainer.RemoveElement(node, true);
if (!windowsContainer.GetCount()) this.deleteNode(rdfNodeWindows);
this.saveStateDelayed();
},
removeAllClosedSession: function SM_removeAllClosedSession() {
for (var i = 0; i < gSessionPath.length; i++) {
var rdfNode = this.RDFService.GetResource(gSessionPath[i]);
var container = this.initContainer(rdfNode);
if (!this.containerEmpty(gSessionPath[i])) this.deleteWithProp(container);
if (!container.GetCount()) this.deleteNode(rdfNode);
}
},
deleteSession: function SM_deleteSession(nodLabel, prop, value) {
var rdfNode = this.RDFService.GetResource(nodLabel);
var container = this.initContainer(rdfNode);
if (!this.containerEmpty(nodLabel)) this.deleteWithProp(container, prop, value);
if (!container.GetCount()) this.deleteNode(rdfNode);
},
deleteWithProp: function(container, prop, value) {
var containerEnum = container.GetElements();
var nodeToDelete = [];
var noProp = typeof(prop) == "undefined";
var valueExist = typeof(value) == "string";
while(containerEnum.hasMoreElements()) {
var node = containerEnum.getNext();
var propExist = noProp ? true : this.nodeHasArc(node, prop);
if (valueExist && !noProp && propExist && this.getLiteralValue(node, prop) != value) propExist = false;
if (propExist) nodeToDelete.push(node);
}
this.deleteArrayNodes(container, nodeToDelete, true);
},
deleteArrayNodes: function(container, nodeToDelete, deleteSubTree) {
for (var i = 0; i < nodeToDelete.length; i++) {
var nodeValue = nodeToDelete[i].QueryInterface(tmRDFResource).Value;
if (deleteSubTree) this.deleteSubtree(nodeValue);
container.RemoveElement(nodeToDelete[i], true);
}
},
destroyMenuItems: function(menu, aRemoveAllItems) {
// Destroy the items.
var destroy = aRemoveAllItems || false, endSeparator;
for (var i = 0; i < menu.childNodes.length; i++) {
var item = menu.childNodes[i];
if (item.id.indexOf("-endSeparator") != -1) {
endSeparator = item
if (menu.parentNode.id != "tm-sessionmanager" &&
menu.parentNode.getAttribute("anonid") != "delete" &&
menu.parentNode.id != "btn_sessionmanager")
break;
else
continue;
}
if (destroy) {
i--;
menu.removeChild(item);
} else if (item.id.indexOf("-startSeparator") != -1) destroy = true;
}
return endSeparator;
},
createMenuForDialog: function(popup, contents) {
if (contents == SHOW_CLOSED_WINDOW_LIST) {
// create closed window list popup menu
this.createMenu(popup, window.opener.gSessionPath[0], contents);
} else {
// create saved Session popup menu
this.createMenu(popup, gRDFRoot+'/windows', contents);
// check if sessionpath and loadsessions valid for saved session
var loadsession = SessionPref.getIntPref("onStart.loadsession");
if (loadsession > -1 && contents != 1 && loadsession != popup.parentNode.sessionIndex) {
SessionPref.setIntPref("onStart.loadsession", popup.parentNode.sessionIndex);
var pref = "onStart.sessionpath";
if (popup.parentNode.sessionIndex < 0 && SessionPref.prefHasUserValue(pref))
SessionPref.clearUserPref(pref);
}
}
},
createMenu: function SM_createMenu(popup, container, contents, aNoSeparators) {
if (popup.id == "btn_closedwindows_menu" && gIsFirefox35) {
var contextmenu = !this.enableManager ? "tm_undocloseWindowContextMenu" : "tm_sessionmanagerContextMenu";
document.getElementById("btn_closedwindows_menu").setAttribute("contextmenu", contextmenu);
if (!this.enableManager) {
HistoryMenu.populateUndoWindowSubmenu("btn_closedwindows_menu");
return;
}
}
if (!this.DATASource) this.initService(); // initService if we call from pref dialog
if (typeof(contents) == "undefined") contents = 0;
var endSeparator = this.destroyMenuItems(popup, aNoSeparators); // Remove any existing menu items
var parentId = popup.parentNode.id;
if (parentId == "btn_sessionmanager" || parentId == "btn_closedwindows")
popup.parentNode.removeAttribute("tooltiptext");
var parentID;
if (popup.parentNode.getAttribute("anonid") == "delete")
parentID = "tm_prompt";
else if (contents != SHOW_CLOSED_WINDOW_LIST)
parentID = popup.parentNode.id;
var aContainer = this.initContainer(container);
var containerEnum = aContainer.GetElements();
var mi, node, name, nameExt, accessKey,index, nodes = [];
var showNameExt = SessionPref.getBoolPref("menu.showext");
var loadsession = SessionPref.getIntPref("onStart.loadsession");
var sessionpath = SessionPref.getCharPref("onStart.sessionpath");
var showTooltip = parentId == "btn_sessionmanager" || parentId == "tm-sessionmanager" || parentId.indexOf("tm-sm-closedwindows")==0 || parentId == "btn_closedwindows";
var closedWinList = parentId.indexOf("closedwindows") != -1;
while(containerEnum.hasMoreElements()) {
node = containerEnum.getNext();
if (this.nodeHasArc(node, "status") &&
this.getLiteralValue(node, "status") != "saved") continue;
nodes.push(node);
}
var count = nodes.length;
for (var i = 0; i < count; i++) {
node = nodes[i];
name = unescape(this.getLiteralValue(node, "name"));
nameExt = this.getLiteralValue(node, "nameExt");
// Insert a menu item for session in the container
mi = document.createElement("menuitem");
mi.session = node.QueryInterface(tmRDFResource).Value;
mi.setAttribute("session", mi.session);
if (contents == 1 && loadsession > -1 && mi.session && mi.session == sessionpath) continue;
mi.setAttribute("value", i);
mi.value = i;
if (parentID != "onStart.loadsession") {
index = closedWinList ? count - 1 - i : i;
accessKey = (index > 25) ? "" : String.fromCharCode(65 + index) + " " ;
mi.setAttribute("accesskey", accessKey);
mi.setAttribute("label", accessKey + name + (showNameExt ? nameExt : ""));
if (showTooltip) mi.setAttribute("tooltiptext", accessKey + name + nameExt);
} else {
mi.setAttribute("label", name);
}
popup.insertBefore(mi, closedWinList ? popup.childNodes[1] : endSeparator);
}
var allEmpty = true;
switch ( parentID ) {
case "tm-sessionmanager":
case "btn_sessionmanager":
var observer = document.getElementById("tmp_menu_AllWindows");
var isOneWindow = (numberOfWindows() == 1);
if (observer.hidden != isOneWindow) observer.hidden = isOneWindow;
case "tm_prompt":
endSeparator.hidden = endSeparator.previousSibling.localName == "menuseparator";
var sessionLabel;
var afterCrash = !this.containerEmpty(gSessionPath[3]);
// if Crashed is empty don't show 'Crashed Session' menu item
if (afterCrash && contents != 1)
sessionLabel = ["lastgood 1","previous 2","crashed 3"];
else
sessionLabel = ["last 1","previous 2"];
var menu;
var bundle_session = document.getElementById("bundle_session_manager");
var empty = ", (" + bundle_session.getString("sm.session.empty") + ")";
for (i = 0; i < sessionLabel.length; i++ ){
menu = document.createElement("menuitem");
var [sLabel , sessionIndex] = sessionLabel[i].split(" ");
menu.session = gSessionPath[sessionIndex];
if (this.containerEmpty(menu.session) && contents != 1)
menu.setAttribute("disabled", "true");
else
allEmpty = false;
nameExt = this.getLiteralValue(menu.session, "nameExt", empty);
sLabel = bundle_session.getString("sm.sessionMenu." + sLabel);
menu.setAttribute("label", sLabel + (showNameExt && contents != 1 ? nameExt : ""));
if (showTooltip) menu.setAttribute("tooltiptext", sLabel + nameExt);
menu.setAttribute("value", (-1 - i));
popup.appendChild (menu);
}
if (afterCrash && contents != 1) { // add separator before Crashed menu item
menu = document.createElement("menuseparator");
popup.insertBefore(menu, popup.lastChild);
}
if (contents == 1) loadsession = -1; //set "Last Sessoin" as default in the list
this.setDefaultIndex(popup, loadsession, sessionpath);
break;
case "onStart.loadsession":
endSeparator.hidden = this.containerEmpty(container);
this.setDefaultIndex(popup, loadsession, sessionpath);
break;
default:
this.setDefaultIndex(popup, loadsession, sessionpath);
if (endSeparator)
endSeparator.hidden = true;
break;
}
var rename = popup.getElementsByAttribute("anonid", "rename")[0];
if (rename)
TMP_setItem(rename, "disabled", count == 0 ? true : null);
var deleteItem = popup.getElementsByAttribute("anonid", "delete")[0];
if (deleteItem)
TMP_setItem(deleteItem, "disabled", allEmpty && count == 0 ? true : null);
},
// set defaultIndex, sessionIndex and default Attribute
setDefaultIndex : function(popup, loadsession, sessionpath) {
popup.parentNode.defaultIndex = -1; // index with menuseparator
popup.parentNode.sessionIndex = -1; // index without menuseparator
var i, item, value, checked;
for (i = 0; i < popup.childNodes.length; i++) {
item = popup.childNodes[i];
if (item.localName == "menuseparator") continue;
value = item.getAttribute("value");
checked = ((loadsession > -1 && item.session && item.session == sessionpath) ||
(loadsession <= -1 && value && value == loadsession));
if (checked) {
item.setAttribute("default", "true");
popup.parentNode.defaultIndex = i;
popup.parentNode.sessionIndex = value;
} else item.removeAttribute("default");
}
},
// update disable/enable to closed window list in tool menu and toolbar
updateClosedWindowsMenu: function(action) {
var disabled = (action == "check") ? this.isClosedWindowsEmpty(): action;
var wnd, enumerator = windowEnumerator();
while ( enumerator.hasMoreElements() ) {
wnd = enumerator.getNext();
var broadcaster = wnd.document.getElementById("tmp_closedwindows");
if (broadcaster)
broadcaster.setAttribute("disabled",disabled);
}
},
isClosedWindowsEmpty: function SM_isClosedWindowsEmpty() {
var node, disabled = true;
var aContainer = this.initContainer(gSessionPath[0]);
var containerEnum = aContainer.GetElements();
while(containerEnum.hasMoreElements()) {
node = containerEnum.getNext();
if (this.getLiteralValue(node, "status") == "saved") {
disabled = false;
break;
}
}
return disabled;
},
// call by init on first window load after crash
openAfterCrash: function SM_openAfterCrash(status) {
var sessionContainer = this.initContainer(gSessionPath[0]);
if (this.enableBackup) {
var path = gRDFRoot + "/closedSession/thisSession";
this.setLiteral(path, "status", "crash2");
// restore to were we was before the crash
var crashedContainer = this.initContainer(gSessionPath[3]);
if (status != "crash2") {
// delete old crash data
if (!this.containerEmpty(gSessionPath[3])) this.deleteWithProp(crashedContainer);
var windowEnum = sessionContainer.GetElements();
var nodeToDelete = [];
while (windowEnum.hasMoreElements()) {
var rdfNodeWindow = windowEnum.getNext();
if (this.getLiteralValue(rdfNodeWindow, "status") == "backup") {
var tabs = this.getResource(rdfNodeWindow, "tabs");
var subTree = rdfNodeWindow.QueryInterface(tmRDFResource).Value;
if (!this.containerEmpty(tabs)) { // copy "backup" subtree to crashed session if it not empty
var newSubTree = subTree.replace(gSessionPath[0], gSessionPath[3]);
this.copySubtree(subTree, newSubTree);
crashedContainer.AppendElement(this.RDFService.GetResource(newSubTree));
}
this.deleteSubtree(subTree); // delete the crashed subtree
nodeToDelete.push(rdfNodeWindow);// remove the window from the crash session
} else this.setLiteral(rdfNodeWindow, "dontLoad", "true"); // we can see this session in the close window list
}
this.deleteArrayNodes(sessionContainer, nodeToDelete, false);
} // if firefox was crashed in middle of crash Recovery try again to restore the same data
else if (!this.containerEmpty(gSessionPath[0]))
this.deleteWithProp(sessionContainer);
var bundle_session = document.getElementById("bundle_session_manager");
var title = bundle_session.getString("sm.afterCrash.title");
var msg;
if (status != "crash2")
msg = bundle_session.getString("sm.afterCrash.msg0");
else
msg = bundle_session.getString("sm.afterCrash.msg0.again");
var chkBoxLabel = !this.enableManager ? bundle_session.getString("sm.afterCrash.chkbox.label") : "";
var buttons, result, whattoLoad = "session";
var chkBoxState = !this.enableManager ? CHECKBOX_UNCHECKED : HIDE_CHECKBOX;
var closedWinList = this.initContainer(gSessionPath[0]).GetCount();
var lastSession = this.containerEmpty(gSessionPath[1]) // last session
var prevtoLast = this.containerEmpty(gSessionPath[2]) // previous to last
var savedSession = this.containerEmpty(gRDFRoot+'/windows') // saved session
var isAllEmpty = lastSession && prevtoLast && savedSession;
if (!this.containerEmpty(gSessionPath[3])) { // if Crashed Session is not empty
var count = this.countWinsAndTabs(crashedContainer);
this.setLiteral(gSessionPath[3], "nameExt", this.getNameData(count.win, count.tab));
if (this.enableManager && !isAllEmpty) {
msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg1");
buttons = [this.setLabel("sm.afterCrash.button0"),
this.setLabel("sm.afterCrash.button1")].join("\n");
result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, SELECT_CRASH],
[title, msg, "", "", buttons]);
} else {
msg += " " + bundle_session.getString("sm.afterCrash.msg2") + ".....";
if (!this.enableManager)
msg += "\n" + bundle_session.getString("sm.afterCrash.msg3");
else
msg += "\n" + bundle_session.getString("sm.afterCrash.msg4");
buttons = [this.setLabel("sm.afterCrash.button0.crashed"),
this.setLabel("sm.afterCrash.button1")].join("\n");
result = TM_PromptService([BUTTON_OK, HIDE_MENUANDTEXT, chkBoxState],
[title, msg, "", chkBoxLabel, buttons]);
result.label = gSessionPath[3];
}
} else {
if (this.enableManager && !isAllEmpty) {
msg += " " + bundle_session.getString("sm.afterCrash.msg5") + "\n\n"
+ bundle_session.getString("sm.afterCrash.msg1");
buttons = [this.setLabel("sm.afterCrash.button0"),
this.setLabel("sm.afterCrash.button1")].join("\n");
result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, SELECT_DEFAULT],
[title, msg, "", "", buttons]);
} else if (closedWinList != 0) {
msg += " " + bundle_session.getString("sm.afterCrash.msg6");
if (!this.enableManager)
msg += "\n" + bundle_session.getString("sm.afterCrash.msg3") + "\n\n"
+ bundle_session.getString("sm.afterCrash.msg7") + ":";
else
msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg7") + " "
+ bundle_session.getString("sm.afterCrash.msg8") + ":";
buttons = [this.setLabel("sm.afterCrash.button0"),
this.setLabel("sm.afterCrash.button1")].join("\n");
result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, chkBoxState, SHOW_CLOSED_WINDOW_LIST],
[title, msg, "", chkBoxLabel, buttons]);
whattoLoad = "closedwindow";
} else {// nothing to restore
msg = bundle_session.getString("sm.afterCrash.msg9") + "\n" + bundle_session.getString("sm.afterCrash.msg10");
if (!this.enableManager)
msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg3");
buttons = ["", this.setLabel("sm.button.continue")].join("\n");
result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
[title, msg, "", chkBoxLabel, buttons]);
}
}
if (result.checked && !this.enableManager) {
SessionPref.setBoolPref("manager", true); // enable session manager
try {
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
} catch(ex) { }
}
if (result.button == BUTTON_OK) {
switch ( whattoLoad ) {
case "session": this.loadSession(result.label, "firstwindowopen");
break;
case "closedwindow": this.openclosedwindow(result.label, true);
break;
default:
}
} else
this.loadHomePage();
} else { // crash recovery is off, delete any remains from the crashed session
if (!this.containerEmpty(gSessionPath[0])) this.deleteWithProp(sessionContainer);
if (this.enableManager) this.openFirstWindow(true); // openFirstWindow with flag openAfterCrash
// else BrowserHome(); // we never get to here...
}
},
// call by init or by openAfterCrash on first window load
openFirstWindow: function SM_openFirstWindow(afterCrash) {
var path = gRDFRoot + "/closedSession/";
var sessionType = ["thisSession", "lastSession", "previoustolastSession", "crashedsession"];
// swap 0 --> 1 --> 2 --> 0
var i;
var sessions = [], subTree, aSession;
for (i = 0; i < sessionType.length-1; i++) {
sessions.push(this.getResource(path + sessionType[i], "session"));
}
for (i = 0; i < sessionType.length-1; i++) {
if (i == 0) { // delete oldest session subtree
aSession = sessions[sessionType.length-2];
subTree = aSession.QueryInterface(tmRDFResource).Value;
this.deleteSubtree(subTree);
} else aSession = sessions[i-1];
this.setResource(path + sessionType[i], "session", aSession)
}
for (i = 0; i < sessionType.length; i++) {
gSessionPath[i] = this.getResourceValue(path + sessionType[i], "session");
}
gThisWin = gSessionPath[0] + "/" + gBrowser.windowID;
gThisWinTabs = gThisWin + "/tabs";
gThisWinClosedtabs = gThisWin + "/closedtabs";
// When Firefox Starts:
// pref "onStart"
// 0 - Restore
// 1 - Ask me before Restore
// 2 (or else) - Don't Restore
//
// pref "onStart.loadsession"
// 0 , 1 , 2 ..... index of saved sessions
// -1, -2 ........ index of previous sessions
//
// if loadsession >= 0 the session path is saved in pref "onStart.sessionpath"
// else if loadsession < 0 the session path is saved in gSessionPath
var restoreFlag = SessionPref.getIntPref("onStart");
if (restoreFlag > 1) {
return; // Don't Restore
}
var loadSession = SessionPref.getIntPref("onStart.loadsession");
// after last session end with restart load the last session without any prompt
// unless we are after crash
var startupEmpty = false, savePref = false ;
var result = {}, title, msg = "", buttons;
var bundle_session = document.getElementById("bundle_session_manager");
if (afterCrash)
title = bundle_session.getString("sm.afterCrash.title");
else
title = bundle_session.getString("sm.start.title");
var chkBoxLabel = afterCrash ? bundle_session.getString("sm.start.chkbox.label") : "";
var chkBoxState = afterCrash ? CHECKBOX_UNCHECKED : HIDE_CHECKBOX;
// get saved session list
var sessionList = this.getSessionList();
var askifempty = restoreFlag > 1 ? false : SessionPref.getBoolPref("onStart.askifempty");
if (sessionList == null) {
if (((askifempty && afterCrash) || restoreFlag == 1) && !this.corruptedFile) {
msg = bundle_session.getString("sm.start.msg0") + "\n"
+ bundle_session.getString("sm.afterCrash.msg10");
if (afterCrash)
msg += "\n\n" + bundle_session.getString("sm.start.msg1");
buttons = ["", this.setLabel("sm.button.continue")].join("\n");
result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
[title, msg, "", chkBoxLabel, buttons]);
if (result.checked && afterCrash) {
SessionPref.setBoolPref("crashRecovery", true); // enable Crash Recovery
nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
}
}
this.loadHomePage();
return;
}
var aList = sessionList.list;
var sessionPath = sessionList.path;
var loadSessionIsValid = true, sessionIndex, thisPath;
switch ( (loadSession > 0) ? 0 : loadSession ) {
case 0:
sessionIndex = null;
if (SessionPref.prefHasUserValue("onStart.sessionpath")) {
thisPath = SessionPref.getCharPref("onStart.sessionpath");
// check if sessionpath is valid
for (i = 0; i < aList.length; i++) {
if (sessionPath[i] == thisPath) {
sessionIndex = i;
break;
}
}
}
if ((thisPath && this.containerEmpty(thisPath)) || sessionIndex == null) {
// error in pref.js or in session.rdf ask the user what to do
loadSessionIsValid = false;
thisPath = gSessionPath[1]; // load last session
SessionPref.setIntPref("onStart.loadsession", -1);
savePref = true;
}
break;
default: // just in case that somehow onStart.loadsession is invalid
loadSession = -1;
SessionPref.setIntPref("onStart.loadsession", -1);
savePref = true;
case -2:
case -1:
var indx = -1 * loadSession;
thisPath = gSessionPath[indx];
if (this.containerEmpty(gSessionPath[indx])) startupEmpty = true;
sessionIndex = aList.length + indx - 3;
break;
}
if (restoreFlag > 0 || afterCrash || (startupEmpty && askifempty) || !loadSessionIsValid) {
try{
if (afterCrash)
msg += bundle_session.getString("sm.afterCrash.msg0") + " "
+ bundle_session.getString("sm.start.msg1");
if (startupEmpty) msg += bundle_session.getString("sm.start.msg0");
if (!loadSessionIsValid) msg += bundle_session.getString("sm.start.msg2");
msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg1");
buttons = [this.setLabel("sm.afterCrash.button0"),
this.setLabel("sm.afterCrash.button1")].join("\n");
result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, chkBoxState, SELECT_DEFAULT],
[title, msg, "", chkBoxLabel, buttons]);
if (result.checked && afterCrash) {
SessionPref.setBoolPref("crashRecovery", true); // enable Crash Recovery
savePref = true;
}
} catch (ex) {TMP_ASSERT(ex);}
}
else {
result.button = startupEmpty ? BUTTON_CANCEL : BUTTON_OK;
result.label = thisPath;
}
if (savePref) nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
if (result.button == BUTTON_OK)
this.loadSession(result.label, "firstwindowopen");
else
this.loadHomePage();
},
getSessionList: function SM_getSessionList(flag) {
var aList = [], sessionPath = [];
var aContainer = this.initContainer(gRDFRoot+'/windows');
var containerEnum = aContainer.GetElements();
var node, aName;
while(containerEnum.hasMoreElements()) {
node = containerEnum.getNext();
aName = this.getLiteralValue(node, "name");
aList.push(aName);
sessionPath.push(node.QueryInterface(tmRDFResource).Value);
}
var bundle_session = document.getElementById("bundle_session_manager");
if (flag == "saved") return {list: aList, path: sessionPath};
else if (flag == "replace") {
aList.push(bundle_session.getString("sm.sessionMenu.lastDefault"));
aList.push(bundle_session.getString("sm.sessionMenu.previous"));
} else {
var empty = ", (" + bundle_session.getString("sm.session.empty") + ")";
var empty1 = this.containerEmpty(gSessionPath[1]);
var empty2 = this.containerEmpty(gSessionPath[2]);
if (empty1 && empty2 && aList.length == 0) return null;
if (flag == "afterCrash") aList.push(bundle_session.getString("sm.sessionMenu.lastgood") + (empty1 ? empty : ""));
else aList.push(bundle_session.getString("sm.sessionMenu.last") + (empty1 ? empty : ""));
aList.push(bundle_session.getString("sm.sessionMenu.previous") + (empty2 ? empty : ""));
}
sessionPath.push(gSessionPath[1]);
sessionPath.push(gSessionPath[2]);
return {list: aList, path: sessionPath};
},
saveAllWindows: function SM_saveAllWindows(path, caller, saveClosedTabs) {
var enumerator = windowEnumerator();
var wnd, savedTabs = 0 , savedWin = 0, thisWin;
while ( enumerator.hasMoreElements() ) {
wnd = enumerator.getNext();
thisWin = wnd.SessionManager.saveOneWindow(path, caller, false, saveClosedTabs);
savedTabs += thisWin;
if (thisWin > 0) savedWin += 1;
}
return {win: savedWin, tab: savedTabs};
},
/*
* update closed window list flag 'dontLoad'
* all window that where closed more then 10 sec ago will mark 'dontLoad'
* return true if we leftout with windows to load
*/
updateClosedWindowList: function SM_updateClosedWindowList(aPopUp) {
var thisSession = this.RDFService.GetResource(gSessionPath[0]);
var container = this.initContainer(thisSession);
var curTime;
if (aPopUp)
curTime = this.getLiteralValue(thisSession, "timestamp", 0);
else {
var pref = "extensions.tabmix.warnAboutClosingTabs.timeout";
var delay = gTabmixPrefs.prefHasUserValue(pref) ? gTabmixPrefs.getCharPref(pref)*1 : 0;
curTime = new Date().valueOf() - delay;
}
var windowEnum = container.GetElements();
while (windowEnum.hasMoreElements()) {
var rdfNodeLastWindow = windowEnum.getNext();
var lastSaved = this.getLiteralValue(rdfNodeLastWindow, "timestamp", 0);
if ((curTime - lastSaved) > 10000) {
if (!this.nodeHasArc(rdfNodeLastWindow, "dontLoad"))
this.setLiteral(rdfNodeLastWindow, "dontLoad", "true");
}
}
var count = this.countWinsAndTabs(container, "dontLoad");
return count.win > 0 && count.tab > 0;
},
saveOneWindow: function SM_saveOneWindow(path, caller, overwriteWindow, saveClosedTabs) {
if (gBrowser.isBlankWindow()) return 0; // dont save window without any tab
if (!path) path = gSessionPath[0];
if (!caller) caller = "";
if (!overwriteWindow) overwriteWindow = false;
if (typeof(saveClosedTabs) == "undefined") saveClosedTabs = this.saveClosedtabs;
// if we going to delete close window from the list we can't use GetCount as ID,
// we need to save unink ID
var winID;
if (caller == "windowclosed" || caller == "windowbackup") winID = gBrowser.windowID;
else winID = this.getAnonymousId();
var winPath = path + "/" + winID;
this.initSession(path, winPath);
var savedTabs = this.saveAllTab(winPath, 0);
if (caller == "windowclosed" && this.enableBackup) {
this.setTabsScroll();
} else {
if (((gThisWin == winPath && !this.enableBackup) || gThisWin != winPath) && saveClosedTabs)
this.copyClosedTabsToRDF(winPath);
}
var rdfNodeThisWindow = this.RDFService.GetResource(winPath);
if (SessionPref.getBoolPref("save.selectedtab")) // save selected tab index
this.setIntLiteral(rdfNodeThisWindow, "selectedIndex", this.getTabPosition());
if (caller == "windowbackup") {
return savedTabs;
}
if (path == gSessionPath[0]) {
// save current tab title. we will use it later in closed windows list as menu entry label
// if current tab is blank get label from first saved tab that isn't blank
var _tab = gBrowser.mCurrentTab;
if (gBrowser.isBlankTab(_tab)) {
for (var i = 0; i < gBrowser.mTabs.length; i++) {
var aTab = gBrowser.mTabs[i];
if (!gBrowser.isBlankTab(aTab)) {
_tab = aTab;
break;
}
}
}
var label = _tab.label;
// replace "Loading..." with the document title (with minimal side-effects)
if (label == gBrowser.mStringBundle.getString("tabs.loading")) {
gBrowser.setTabTitle(_tab);
[label, _tab.label] = [_tab.label, label];
}
this.setLiteral(rdfNodeThisWindow, "name", escape(label));
this.setLiteral(rdfNodeThisWindow, "nameExt", this.getNameData(-1, savedTabs));
var pref = "extensions.tabmix.warnAboutClosingTabs.timeout";
var delay = gTabmixPrefs.prefHasUserValue(pref) ? gTabmixPrefs.getCharPref(pref)*1 : 0;
var newTime = new Date().valueOf() - delay;
this.setLiteral(rdfNodeThisWindow, "timestamp", newTime);
// if we overwrite window we don't load it again on restart
if (this.overwriteWindow || overwriteWindow)
this.setLiteral(rdfNodeThisWindow, "dontLoad", "true");
// when we save on close we set this in windowIsClosing
if (caller != "windowclosed") {
this.setLiteral(rdfNodeThisWindow, "status", "saved");
this.updateClosedWindowsMenu(false);
}
}
else
this.setLiteral(rdfNodeThisWindow, "status", "");
this.saveStateDelayed();
return savedTabs;
}, // end of "saveOneWindow : function ()"
// if this session is not in the container add it to the last place and init prop
// else move it to the last place
initSession: function SM_initSession(path, winPath) {
var container = this.initContainer(path);
var rdfNode = this.RDFService.GetResource(winPath);
var index = container.IndexOf(rdfNode);
if (index == -1) {
container.AppendElement(rdfNode);
this.setLiteral(rdfNode, "status", "backup");
this.setResource(rdfNode, "tabs", winPath + "/tabs");
this.setResource(rdfNode, "closedtabs", winPath + "/closedtabs");
} else if (index != container.GetCount()) {
container.RemoveElementAt(index, true);
container.AppendElement(rdfNode);
}
},
// xxx need to fix this to save only history, image and history index
// and save the rest when tab added
tabLoaded: function SM_tabLoaded(aTab) {
if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
if (gBrowser.isBlankTab(aTab)) return;
// if this window is not in the container add it to the last place
this.initSession(gSessionPath[0], gThisWin);
var tabContainer = this.initContainer(gThisWinTabs);
var result = this.saveTab(aTab, gThisWinTabs, tabContainer, true, 0);
if (result)
this.saveStateDelayed(-1);
},
updateTabPos: function(aTab, label, add0_1) {
var tab, node;
if (!add0_1) add0_1 = 0;
for (var i = aTab._tPos + add0_1; i < gBrowser.mTabs.length; i++) {
tab = gBrowser.mTabs[i];
node = (typeof(label) == "undefined") ? this.getNodeForTab(tab) : label + "/" + tab.linkedPanel;
this.setIntLiteral(node, "tabPos", tab._tPos);
}
},
tabClosed: function SM_tabClosed(aTab) { // delete tab from container and save to closed tab list backup
// we don't check aTab.hasAttribute("inrestore") , in case tab is closed befor
// its finish to restore.
if (!this.enableBackup) return;
this.initSession(gSessionPath[0], gThisWin);
var tabContainer = this.initContainer(gThisWinTabs);
var panelPath = this.getNodeForTab(aTab);
var nodeToClose = this.RDFService.GetResource(panelPath);
this.updateTabPos(aTab); // update _tPos for the tab right to the deleted tab
if (this.saveClosedtabs) {
// move closedtabs to closedtabs container
var closedTabContainer = this.initContainer(gThisWinClosedtabs);
var tabExist = true;
if (tabContainer.IndexOf(nodeToClose) == -1) {
tabExist = this.saveTab(aTab, gThisWinTabs, closedTabContainer, false, 0);
} else tabContainer.RemoveElement(nodeToClose, true);
if (tabExist) {
closedTabContainer.AppendElement(nodeToClose);
if (closedTabContainer.GetCount() > gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo"))
this.deleteClosedtabAt(1, gThisWin);
}
} else if (tabContainer.IndexOf(nodeToClose) > -1) {
// xxx try to use deleteSession ????
this.deleteSubtree(panelPath);
tabContainer.RemoveElement(nodeToClose, true);
// xxx is it necessary to delete this ???
if (!tabContainer.GetCount()) { // if no tab in the container remove it from the tree
var winContainer = this.initContainer(gSessionPath[0]);
var rdfNode = this.RDFService.GetResource(gThisWin);
winContainer.RemoveElement(rdfNode, true);
this.deleteSubtree(gThisWin);
}
}
this.saveStateDelayed(-1);
},
updateTabProp: function SM_updateTabProp(aTab) {
// we dont need this function to run before sessionmanager init
// this.enableBackup=null so we dont past the next if
if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
if (gBrowser.isBlankTab(aTab)) return; // dont write blank tab to the file
this.initSession(gSessionPath[0], gThisWin);
this.setLiteral(this.getNodeForTab(aTab), "properties", SessionData.getTabProperties(aTab, true));
this.saveStateDelayed();
},
tabMoved: function SM_tabMoved(aTab, oldPos, newPos) {
if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
this.initSession(gSessionPath[0], gThisWin);
// can't use aTab._tPos after group of tab delete
// we pass old position and new position from TMmoveTabTo
// we need to fix tabPos for all tab between old position and new position
var first = Math.min(oldPos, newPos);
var last = Math.max(oldPos, newPos);
for (var i = first; i < last + 1; i++) {
var tab = gBrowser.mTabs[i];
if (!gBrowser.isBlankTab(tab))
this.setIntLiteral(this.getNodeForTab(tab), "tabPos", i);
}
this.saveStateDelayed();
},
setTabsScroll: function() {
if (SessionPref.getBoolPref("save.scrollposition"))
for (var i = 0; i < gBrowser.mTabs.length; i++)
this.tabScrolled(gBrowser.mTabs[i]);
},
// xxx need to find the right event to trigger this function..
tabScrolled: function SM_tabScrolled(aTab) {
if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
var aBrowser = gBrowser.getBrowserForTab(aTab);
if (gBrowser.isBlankBrowser(aBrowser)) return;
var bContent = aBrowser.contentWindow;
var zoomFactor = aBrowser.docShell.contentViewer ? aBrowser.markupDocumentViewer.textZoom : 1;
this.setLiteral(this.getNodeForTab(aTab), "scroll", bContent.scrollX + "," + bContent.scrollY + "," + zoomFactor);
},
tabSelected: function(needFlush) {
if (!this.enableBackup || gBrowser.mCurrentTab.hasAttribute("inrestore")) return;
if (typeof(needFlush) == "undefined") needFlush = false;
this.initSession(gSessionPath[0], gThisWin);
this.setTabsScroll(); // until i find proper event to update tab scroll do it from here
if (SessionPref.getBoolPref("save.selectedtab")) {
this.setIntLiteral(gThisWin, "selectedIndex", this.getTabPosition());
}
if (needFlush)
this.saveStateDelayed();
},
getTabPosition: function() { // calc selected tab position if blank tab not restore
if (gBrowser.isBlankTab(gBrowser.mCurrentTab)) return 0; // if the current tab is blank we don't resore the index
var blankTab = 0;
for (var i = 0; i < gBrowser.mCurrentTab._tPos; i++) {
if (gBrowser.isBlankTab(gBrowser.mTabs[i])) blankTab++;
}
return gBrowser.mCurrentTab._tPos - blankTab;
},
getNodeForTab: function(aTab) {
return gThisWinTabs + "/" + aTab.linkedPanel;
},
saveAllTab: function SM_saveAllTab(winPath, offset, saveBusy) {
var savedTabs = 0 ;
var rdfNodeTabs = this.getResource(winPath, "tabs");
var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
var tabContainer = this.initContainer(rdfNodeTabs);
for (var i = 0; i < gBrowser.mTabs.length; i++) {
var aTab = gBrowser.mTabs[i];
if (saveBusy && !aTab.hasAttribute("busy")) continue; // save only busy tabs
if (this.saveTab(aTab, rdfLabelTabs, tabContainer, true, offset)) savedTabs ++;
}
return savedTabs;
},
// call from tabloaded, tabClosed, saveAllTab
// xxx add flag what to save : all, history, property, scrollPosition
saveTab: function SM_saveTab(aTab, rdfLabelTabs, tabContainer, needToAppend, offset) {
var aBrowser = gBrowser.getBrowserForTab(aTab);
if (gBrowser.isBlankBrowser(aBrowser)) return false;
// clear sanitized flag
if (SessionPref.prefHasUserValue("sanitized")) {
SessionPref.clearUserPref("sanitized");
this.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "crash");
}
var sessionHistory = aBrowser.webNavigation.sessionHistory;
var rdfLabelTab = rdfLabelTabs + "/" + aTab.linkedPanel;
var index = sessionHistory.index < 0 ? 0 : sessionHistory.index;
var bContent = aBrowser.contentWindow;
var zoomFactor = aBrowser.docShell.contentViewer ? aBrowser.markupDocumentViewer.textZoom : 1;
try {
var curHistory = sessionHistory.getEntryAtIndex(index, false);
curHistory.QueryInterface(tmSHEntry).setScrollPosition(bContent.scrollX, bContent.scrollY);
} catch (e) {TMP_ASSERT(ex, "saveTab error at index " + sessionHistory.index);}
var rdfNodeTab = this.RDFService.GetResource(rdfLabelTab);
var data = {
index: this.enableSaveHistory ? index : 0,
pos: aTab._tPos + offset,
image: aTab.getAttribute("image"),
properties: SessionData.getTabProperties(aTab, true),
history: this.saveTabHistory(sessionHistory),
scroll: bContent.scrollX + "," + bContent.scrollY + "," + zoomFactor
};
this.saveTabData(rdfNodeTab, data);
// dont append if we call from tabClosed function
//XXX move this to the caller function
if (tabContainer.IndexOf(rdfNodeTab) == -1 && needToAppend) {
tabContainer.AppendElement(rdfNodeTab);
this.updateTabPos(aTab, rdfLabelTabs, 1); // update _tPos for the tab right to the new tab
}
return true;
},
saveTabData: function SM_saveTabData(aNode, aData) {
this.setIntLiteral(aNode, "index", aData.index);
this.setIntLiteral(aNode, "tabPos", aData.pos);
this.setLiteral (aNode, "image", aData.image || ""); // for use in closed tab list
this.setLiteral (aNode, "properties", aData.properties);
this.setLiteral (aNode, "history", aData.history);
this.setLiteral (aNode, "scroll", aData.scroll);
},
// xxx save text size (zoom), char type ?
saveTabHistory: function(sessionHistory) {
var historyStart = this.enableSaveHistory ? 0 : sessionHistory.index;
var historyEnd = this.enableSaveHistory ? sessionHistory.count : sessionHistory.index+1;
var j, historyEntry, history = [];
for (j = historyStart; j < historyEnd; j++) {
try {
historyEntry = sessionHistory.getEntryAtIndex(j, false).QueryInterface(tmSHEntry);
history.push(escape(historyEntry.title));
history.push(historyEntry.URI.spec);
history.push(this.getScrollPosHs(historyEntry)); // not in use yet
} catch (ex) {TMP_ASSERT(ex, "saveTabHistory error at index " + j); }
}
// generate unique separator and combine the array to one string
var separator = "][", extraSeparator = "@";
for (var i = 0; i < history.length; ++i) {
while (history[i].indexOf(separator) > -1)
separator += extraSeparator;
}
// insert the separator to history so we can extract it in loadTabHistory
return separator + "|-|" + history.join(separator);
},
getScrollPosHs: function(historyEntry) {
if (SessionPref.getBoolPref("save.scrollposition")) {
var x={}, y={};
historyEntry.getScrollPosition(x, y);
return x.value + "," + y.value;
} return "0,0";
},
loadSession: function SM_loadSession(path, caller, overwriteWindows) {
var sessionContainer = this.initContainer(path);
var sessionEnum = sessionContainer.GetElements();
var sessionCount = 0, concatenate;
var windowEnum = windowEnumerator();
if (typeof(overwriteWindows) == "undefined")
overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows");
// don't concatenate window after crash
if (caller == "firstwindowopen" && this.getLiteralValue(gSessionPath[0], "status") == "crash2")
concatenate = false;
else
concatenate = SessionPref.getBoolPref("restore.concatenate");
var saveBeforOverwrite = SessionPref.getBoolPref("restore.saveoverwrite");
var overwriteTabs = SessionPref.getBoolPref("restore.overwritetabs");
// in single window mode we restore ALL window into this window
if (gSingleWindowMode)
concatenate = true;
// if this window is blank use it when reload session
var wnd, blankWindow;
if (!gSingleWindowMode && concatenate && !overwriteWindows && !gBrowser.isBlankWindow() && caller != "firstwindowopen" && caller != "concatenatewindows") {
this.openNewWindow(path, "concatenatewindows");
return;
}
// if we join all window to one window
// call the same window for all saved window with overwritewindows=false and overwritetabs=false if this not the first saved
// for first saved window overwritetabs determined by user pref
while (sessionEnum.hasMoreElements()) {
sessionCount++;
var rdfNodeSession = sessionEnum.getNext();
if (rdfNodeSession instanceof tmRDFResource) {
var windowPath = rdfNodeSession.QueryInterface(tmRDFResource).Value;
if (this.nodeHasArc(windowPath, "dontLoad")) continue;
if (concatenate) {
if (caller != "concatenatewindows" && caller != "firstwindowopen" && sessionCount == 1
&& saveBeforOverwrite && overwriteTabs)
this.saveOneWindow(gSessionPath[0], "", true);
var newCaller = (sessionCount != 1) ? caller+"-concatenate" : caller;
this.loadOneWindow(windowPath, newCaller);
} else {
wnd = null;
blankWindow = false;
if (windowEnum.hasMoreElements()) {
wnd = windowEnum.getNext();
blankWindow = wnd.gBrowser.isBlankWindow();
}
if (wnd != null && (overwriteWindows || blankWindow || (caller == "firstwindowopen" && sessionCount == 1 ))) {
// if we save overwrite windows in the closed windows list don't forget to set dontLoad==true
if (caller != "firstwindowopen" && saveBeforOverwrite && overwriteTabs)
wnd.SessionManager.saveOneWindow(gSessionPath[0], "", true);
wnd.SessionManager.loadOneWindow(windowPath, caller);
} else
this.openNewWindow(windowPath, caller);
}
}
}
// cloes extra windows if we overwrite open windows and set dontLoad==true
if (numberOfWindows() > 1 && overwriteWindows) {
while (windowEnum.hasMoreElements()) {
wnd = windowEnum.getNext();
if (concatenate && wnd == window) continue;
if (saveBeforOverwrite) wnd.SessionManager.overwriteWindow = true;
else wnd.SessionManager.saveThisWindow = false;
wnd.close();
}
}
},
openclosedwindow: function SM_openclosedwindow(path, overwriteWindows) {
// 1. check if to overwrite the opener window
// if 1 is true call loadOneWindow
// if 1 is false open new window and pass the path
// 2. delete the window from closedwindow list (after new window is opend and load)
var rdfNodeClosedWindow = this.RDFService.GetResource(path);
// don't reopen same window again. the window removed from closed window list after it finish to load
if (this.nodeHasArc(rdfNodeClosedWindow, "reOpened")) return;
this.setLiteral(rdfNodeClosedWindow, "reOpened", "true");
if (typeof(overwriteWindows) == "undefined") overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows");
var saveBeforOverwrite = SessionPref.getBoolPref("restore.saveoverwrite");
var overwriteTabs = SessionPref.getBoolPref("restore.overwritetabs");
if (overwriteWindows || gBrowser.isBlankWindow() || gSingleWindowMode) {
if (saveBeforOverwrite && overwriteTabs)
this.saveOneWindow(gSessionPath[0], "", true);
this.loadOneWindow(path, "openclosedwindow");
} else
this.openNewWindow(path, "openclosedwindow");
this.saveStateDelayed();
},
openNewWindow: function SM_openNewWindow(path, caller) {
var newWindow = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", null);
newWindow.tabmixdata = { path: path, caller: caller };
},
loadOneWindow: function SM_loadOneWindow(path, caller) {
var overwrite = true, restoreSelect = SessionPref.getBoolPref("save.selectedtab");
switch ( caller ) {
case "firstwindowopen":
if (window.arguments && window.arguments.length > 0) {
overwrite = window.arguments[0] == gHomeButton.getHomePage() ? true : false ;
if (!overwrite && window.arguments[0] != "about:blank") restoreSelect = false;
} else overwrite = false;
break;
case "windowopenebytabmix":
case "concatenatewindows":
overwrite = true;
break;
case "openclosedwindow":
case "sessionrestore":
overwrite = SessionPref.getBoolPref("restore.overwritetabs");
break;
case "firstwindowopen-concatenate":
case "openclosedwindow-concatenate":
case "sessionrestore-concatenate":
case "concatenatewindows-concatenate":
overwrite = false;
break;
default: tmLog("SessionManager \n error unidentifid caller " + caller);
}
/*
1. when open first windows overwrite tab only if they are home page, if firefox open from link or with
pages that are not the home page append the new tab to the end.
simple solution is to set browser.startup.page = 0 , when we activate session manager, in this case if we
have any tabs in the first window we don't overwrite.
2. when open window by session manager other than the first window (caller = "windowopenebytabmix" and tabmix in the name) overwrite=true
3. when loadOneWindow call by openclosedwindow or loadSession we reuse window check user pref for overwrite.
4. if we open all closed windows to one window append tab to the end and select the selected tab from first window
in the session.
*/
var cTab = gBrowser.mCurrentTab;
var concatenate = caller.indexOf("-concatenate") != -1 || (caller == "firstwindowopen" && gBrowser.mTabContainer.childNodes.length > 1);
var rdfNodeWindow = this.RDFService.GetResource(path);
var rdfNodeTabs = this.getResource(rdfNodeWindow, "tabs");
if (!(rdfNodeTabs instanceof tmRDFResource) || this.containerEmpty(rdfNodeTabs)) {
var bundle_session = document.getElementById("bundle_session_manager");
alert(bundle_session.getString("sm.restoreError.msg0") + "\n" + bundle_session.getString("sm.restoreError.msg1"));
var stringBundle = document.getElementById("tmp-string-bundle");
var tabmix_loading = stringBundle.getString("session.loading.label") + "...";
if (gBrowser.mCurrentTab.label == tabmix_loading)
gBrowser.selectedBrowser.reload();
return;
}
var tabContainer = this.initContainer(rdfNodeTabs);
var newtabsCount = tabContainer.GetCount();
gBrowser.tabsToLoad = newtabsCount;
gBrowser.mTabContainer.nextTab = 1;
this.setStripVisibility(newtabsCount);
var lastSelectedIndex = restoreSelect ? this.getIntValue(rdfNodeWindow, "selectedIndex") : 0;
if (lastSelectedIndex < 0 || lastSelectedIndex >= newtabsCount) lastSelectedIndex = 0;
var i, newIndex, aTab, tabPos;
if (overwrite) {
if (gBrowser.mTabContainer.collapsedTabs > 0)
gBrowser.mTabContainer.collapsedTabs = 0;
for ( i = 0; i < gBrowser.mTabContainer.childNodes.length; i++) {
aTab = gBrowser.mTabContainer.childNodes[i];
var aBrowser = gBrowser.getBrowserForTab(aTab);
// reset old history
aBrowser.webNavigation.sessionHistory =
Components.classes["@mozilla.org/browser/shistory;1"]
.createInstance(tmSHistory);
// remove selected and flst_id from all tabs but the current
if (aTab != cTab) {
aTab.removeAttribute("visited");
aTab.removeAttribute("flst_id");
}
// if we need to remove extra tabs make sure they are not protected
if (aTab.hasAttribute("protected"))
aTab.removeAttribute("protected");
}
while (newtabsCount > gBrowser.mTabContainer.childNodes.length) {
gBrowser.addTab("about:blank");
}
// move selected tab to place
gBrowser.TMmoveTabTo(cTab, lastSelectedIndex);
// remove extra tab
while (newtabsCount < gBrowser.mTabContainer.childNodes.length) {
gBrowser.removeTab(gBrowser.mTabContainer.lastChild);
}
newIndex = 0;
} else {
// reuse blank tabs and move tabs to the right place
var openTabNext = gTabmixPrefs.getBoolPref("extensions.tabmix.openTabNext");
// catch blank tab for reuse
var blankTabs = [], blankTabsCount = 0, currentTabIsBalnk = false;
for (i = 0; i < gBrowser.mTabContainer.childNodes.length ; i++) {
aTab = gBrowser.mTabContainer.childNodes[i];
if (!aTab.loadOnStartup) { // make sure we not overwrite tab that loads from apps
if (gBrowser.isBlankTab(aTab) && (aTab.hasAttribute("tabmix_busy") || !aTab.hasAttribute("busy"))) {
aTab.removeAttribute("tabmix_busy");
if (aTab != cTab) {
blankTabs.push(aTab);
aTab.removeAttribute("visited");
aTab.removeAttribute("flst_id");
} else {
blankTabs.unshift(aTab);
currentTabIsBalnk = true;
}
}
} else delete aTab.loadOnStartup;
}
// remove extra tabs
var blankTab;
while (blankTabs.length > newtabsCount) {
blankTab = blankTabs.pop();
if (blankTab) gBrowser.removeTab(blankTab);
}
var newPos = (openTabNext && cTab._tPos < gBrowser.mTabContainer.childNodes.length - 1 && !concatenate) ? cTab._tPos + 1 : gBrowser.mTabContainer.childNodes.length - 1;
// move blank tabs to new position
blankTabsCount = blankTabs.length;
while (blankTabs.length > 0) {
blankTab = blankTabs.shift();
tabPos = (blankTab._tPos < newPos && newPos < gBrowser.mTabContainer.childNodes.length - 1) ? newPos - 1 : newPos;
gBrowser.TMmoveTabTo(blankTab, tabPos);
}
var tabsCount = gBrowser.mTabContainer.childNodes.length;
var newTotalTabsCount = tabsCount - blankTabsCount + newtabsCount;
while (newTotalTabsCount > gBrowser.mTabContainer.childNodes.length) {
var newTab = gBrowser.addTab("about:blank");
// in concatenate mode move tab to the end of the list
if (concatenate && openTabNext) {
gBrowser.TMmoveTabTo(newTab, gBrowser.mTabContainer.childNodes.length-1);
}
}
if (tabsCount == blankTabsCount) newPos = 0;
else newPos = (openTabNext && cTab._tPos < gBrowser.mTabContainer.childNodes.length - 1 && !concatenate) ? cTab._tPos + 1 : tabsCount - blankTabsCount;
if (!concatenate && restoreSelect) { // in concatenate mode we select tab only from first window
if (currentTabIsBalnk) { // if the current tab is not blank select new tab
if (openTabNext && newPos > 0) newPos--;
// move selected tab to place
gBrowser.TMmoveTabTo(cTab, newPos + lastSelectedIndex);
}
else
this.updateSelected(newPos + lastSelectedIndex, caller=="firstwindowopen" || caller=="windowopenebytabmix");
}
newIndex = newPos;
}
// call ensureTabIsVisible before and after we reload the tab
gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);
var tabsEnum = tabContainer.GetElements();
// sort the tab by "tabPos"
var rdfTabs = [], rdfNodeTab;
while (tabsEnum.hasMoreElements()) {
rdfNodeTab = tabsEnum.getNext();
if (rdfNodeTab instanceof tmRDFResource) {
tabPos = this.getIntValue(rdfNodeTab, "tabPos");
rdfTabs.push([tabPos, rdfNodeTab]);
}
}
rdfTabs.sort(this.sortByColumn(0 ,true));
this.initSession(gSessionPath[0], gThisWin); // init the new container before we start to load data
// restore the selected tab first
var selectedTabLoaded;
if (restoreSelect && lastSelectedIndex in rdfTabs) {
selectedTabLoaded = true;
aTab = gBrowser.mTabContainer.childNodes[newIndex + lastSelectedIndex];
this.loadOneTab(rdfTabs[lastSelectedIndex][1], aTab);
}
for (i = 0; i < rdfTabs.length ; i++) {
if (selectedTabLoaded && lastSelectedIndex == i)
continue;
aTab = gBrowser.mTabContainer.childNodes[newIndex + i];
this.loadOneTab(rdfTabs[i][1], aTab);
}
if (this.saveClosedtabs)
this.saveClosedTabs(path, gThisWin, "closedtabs", true); // load prev saved closed tabs and save to current backup
gBrowser.mTabContainer.nextTab = 1;
// if we open closed window delete this window from closed window list
var caller1;
if ("tabmixdata" in window) {
caller1 = window.tabmixdata.caller;
delete window.tabmixdata;
}
if (caller == "openclosedwindow" || caller1 == "openclosedwindow"){
if (this.nodeHasArc(rdfNodeWindow, "reOpened")) {
this.removeSession(path, gSessionPath[0]);
this.updateClosedWindowsMenu("check");
}
}
},
updateSelected: function(newIndex, removeAttribute) {
let oldIndex = gBrowser.mTabContainer.selectedIndex;
if (newIndex != oldIndex) {
let tabs = gBrowser.mTabContainer.childNodes;
gBrowser.selectedTab = tabs[newIndex];
if (removeAttribute) {
tabs[oldIndex].removeAttribute("visited");
tabs[oldIndex].removeAttribute("flst_id");
}
}
},
setStripVisibility: function(tabCount) {
if (tabCount > 1 && gTabmixPrefs.getBoolPref("browser.tabs.autoHide") && !gBrowser.getStripVisibility()) {
// unhide the tab bar
gBrowser.setStripVisibilityTo(true);
// forceHide was removed in Firefox 3.5
if (gTabmixPrefs.prefHasUserValue("browser.tabs.forceHide"))
gTabmixPrefs.clearUserPref("browser.tabs.forceHide")
}
},
sortByColumn: function(nCol, bDescending) {
var c = nCol;
var d = bDescending;
return function (n1, n2) {
if (n1[c] < n2[c]) return (d) ? -1 : +1;
if (n1[c] > n2[c]) return (d) ? +1 : -1;
return 0;
}
},
saveClosedTabs: function SM_saveClosedTabs(fromPath, toPath, conPath, updateClosedTabsList) {
var isClosedTabs = conPath == "closedtabs";
if (isClosedTabs && !(this.saveClosedtabs))
return;
var fromOld = this.wrapContainer(fromPath, conPath);
if (!(fromOld.Root instanceof tmRDFResource)) return;
var toNew = this.wrapContainer(toPath, conPath);
var rdfNodeTabs = this.getResource(toPath, "tabs");
var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
var maxTabsUndo = gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
var newIndex = -1;
while (fromOld.Enum.hasMoreElements()) {
var rdfNodeSession = fromOld.Enum.getNext();
if (!(rdfNodeSession instanceof tmRDFResource)) continue;
newIndex++;
if (isClosedTabs && (fromOld.Count - newIndex > gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo"))) continue;
var uniqueId = "panel" + Date.now() + newIndex;
var rdfLabelSession = rdfLabelTabs + "/" + uniqueId;
var newNode = this.RDFService.GetResource(rdfLabelSession);
var data = {}
data.pos = this.getIntValue(rdfNodeSession, "tabPos");
data.image = this.getLiteralValue(rdfNodeSession, "image");
data.properties = this.getLiteralValue(rdfNodeSession, "properties");
data.scroll = this.getLiteralValue(rdfNodeSession, "scroll"); // including zoom factor
if (this.enableBackup) { // save only if backup enabled
toNew.Container.AppendElement(newNode);
data.index = this.getIntValue(rdfNodeSession, "index");
data.history = this.getLiteralValue(rdfNodeSession, "history");
this.saveTabData(newNode, data);
// delete old entry if closedTabs container wasn't empty
if (isClosedTabs && (toNew.Container.GetCount() > maxTabsUndo))
this.deleteClosedtabAt(1, toPath);
}
if(updateClosedTabsList && !TMP_ClosedTabs.ssIsON) {
var savedHistory = this.loadTabHistory(rdfNodeSession);
data.history = savedHistory.history;
if (savedHistory == null) {
tmLog("closed tab at index " + newIndex + " failed to load data from the saved session");
continue;
}
try {
data.title = savedHistory.history.getEntryAtIndex(savedHistory.index, true).title;
if (!SessionPref.getBoolPref("save.scrollposition"))
data.scroll = "0,0,1";
else if (data.scroll.split(",").length < 3) // version before 0.3.0.603 don't include zoomfactor
data.scroll += ",1"
gBrowser.closedTabs.unshift(data);
} catch (ex) {TMP_ASSERT(ex, "saveClosedTabs error at index " + savedHistory.index); }
// delete old entry if gBrowser.closedTabs wasn't empty
var tabsCount = gBrowser.closedTabs.length;
if (tabsCount > maxTabsUndo)
gBrowser.closedTabs.splice(maxTabsUndo, tabsCount - maxTabsUndo);
}
}
if (updateClosedTabsList) {
// if we after restart we get closed data from FF sessionRestore
if (TMP_ClosedTabs.ssIsON && !TMP_SessionStore.isAfterRestart()) {
var state = { windows: [], _firstTabs: true };
state.windows[0] = { _closedTabs: [] };
state.windows[0]._closedTabs = convertSession.getClosedTabsState(this.getResource(fromPath, "closedtabs"));
TMP_ClosedTabs.ss.setWindowState(window, state.toSource(), false);
}
}
},
copyClosedTabsToRDF: function SM_copyClosedTabsToRDF(winPath) {
var rdfNodeTo = this.getResource(winPath, "closedtabs");
var toContainer = this.initContainer(rdfNodeTo);
var rdfNodeTabs = this.getResource(winPath, "tabs");
var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
var ctabs = TMP_ClosedTabs.getClosedTabData;
var tabCount = ctabs.length;
var maxTabsUndo = gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
var tabData, uniqueId, rdfLabelSession, newNode, historyEntry, scrollPos, history;
for (var i = tabCount - 1; i >= 0; i--) {
uniqueId = "panel" + Date.now() + i;
rdfLabelSession = rdfLabelTabs + "/" + uniqueId;
newNode = this.RDFService.GetResource(rdfLabelSession);
toContainer.AppendElement(newNode);
tabData = ctabs[i];
if (TMP_ClosedTabs.ssIsON) {
this.getSessionStoreDataForRDF(tabData);
}
else {
tabData.index = tabData.history.index;
// convert history object into string
tabData.history = this.saveTabHistory(tabData.history);
if (!SessionPref.getBoolPref("save.scrollposition"))
tabData.scroll = "0,0,1";
}
this.saveTabData(newNode, tabData);
// delete old entry if closedTabs container wasn't empty
if (toContainer.GetCount() > maxTabsUndo)
this.deleteClosedtabAt(1, winPath);
}
this.saveStateDelayed();
},
getSessionStoreDataForRDF: function SM_getSessionStoreDataForRDF(aTabData) {
var tabState = aTabData.state;
var count = tabState.entries.length;
var activeIndex = (tabState.index || count) - 1;
var historyStart = this.enableSaveHistory ? 0 : activeIndex;
var historyEnd = this.enableSaveHistory ? count : activeIndex + 1;
var j, historyEntry, history = [];
for (j = historyStart; j < historyEnd; j++) {
try {
historyEntry = tabState.entries[j];
history.push(escape(historyEntry.title));
history.push(historyEntry.url);
history.push(historyEntry.scroll || "0,0"); // not in use yet
} catch (ex) {TMP_ASSERT(ex, "saveTabHistory error at index " + j); }
}
// generate unique separator and combine the array to one string
var separator = "][", extraSeparator = "@";
for (var i = 0; i < history.length; ++i) {
while (history[i].indexOf(separator) > -1)
separator += extraSeparator;
}
// insert the separator to history so we can extract it in loadTabHistory
aTabData.history = separator + "|-|" + history.join(separator);
aTabData.index = this.enableSaveHistory ? activeIndex : 0,
aTabData.scroll = SessionPref.getBoolPref("save.scrollposition") ?
(tabState.entries[activeIndex].scroll || "0,0") + "," + (aTabData.zoom || 1) : "0,0,1";
// closed tab can not be protected - set protected to 0
var _locked = TMP_SessionStore._getAttribute(tabState, "_locked") != "false" ? "1" : "0";
aTabData.properties = "0" + _locked;
if ("disallow" in tabState && tabState.disallow) {
for (var j = 0; j < SessionData.docShellItems.length; j++ )
aTabData.properties += tabState.disallow.indexOf(SessionData.docShellItems[j]) == -1 ? "1" : "0";
}
else {
aTabData.properties += "11111";
}
if ("attributes" in tabState && tabState.attributes) {
delete tabState.attributes["_locked"];
for (var name in tabState.attributes) {
aTabData.properties += " " + name + "=" + encodeURI(tabState.attributes[name]);
}
}
if ("xultab" in tabState && tabState.xultab) {
tabState.xultab = tabState.xultab.replace(" _locked=true", "").replace(" _locked=false", "");
if (tabState.xultab)
aTabData.properties += " " + tabState.xultab;
}
},
deleteAllClosedtabs: function(sessionContainer) { // delete all closed tabs in this session
var windowEnum = sessionContainer.GetElements();
while (windowEnum.hasMoreElements()) {
var rdfNodeWindow = windowEnum.getNext();
this.deleteWinClosedtabs(rdfNodeWindow.QueryInterface(tmRDFResource).Value);
}
},
deleteWinClosedtabs: function SM_deleteWinClosedtabs(winPath) {
var rdfNodeTabs = this.getResource(winPath, "closedtabs");
var container = this.initContainer(rdfNodeTabs);
this.deleteWithProp(container);
this.saveStateDelayed();
},
deleteClosedtabAt: function SM_deleteClosedtabAt(index, winPath) {
if (!SessionPref.getBoolPref("save.closedtabs"))
return;
if (typeof(winPath) == 'undefined')
winPath = gThisWin;
var rdfNodeTabs = this.getResource(winPath, "closedtabs");
var container = this.initContainer(rdfNodeTabs);
if (index == "last")
index = container.GetCount();
if (index < 1 || index > container.GetCount())
return;
var nodeToDelete = container.RemoveElementAt(index, true);
var nodeValue = nodeToDelete.QueryInterface(tmRDFResource).Value
this.deleteSubtree(nodeValue);
if (!container.GetCount()) this.deleteNode(rdfNodeTabs);
this.saveStateDelayed();
},
loadOneTab: function SM_loadOneTab(rdfNodeSession, aTab) {
aTab.setAttribute("inrestore", "true"); // flag. dont save tab that are in restore phase
// load Properties before we load History
var tabProperties = this.getLiteralValue(rdfNodeSession, "properties");
if (tabProperties != "") SessionData.setTabProperties(aTab, tabProperties, true);
var aBrowser = gBrowser.getBrowserForTab(aTab);
var webNav = aBrowser.webNavigation;
var savedHistory = this.loadTabHistory(rdfNodeSession, webNav.sessionHistory);
if (savedHistory == null) {
tmLog("loadOneTab() - tab at index " + aTab._tPos + " failed to load data from the saved session");
gBrowser.removeTab(aTab);
return;
}
aBrowser._sessionData = {
node: rdfNodeSession
};
try {
// if url is file and file don't exist it throws an exception, we don't need to reload loacl file
var url = webNav.sessionHistory.getEntryAtIndex(savedHistory.index, false).URI.spec;
var needToReload = SessionPref.getBoolPref("restore.reloadall") && url.indexOf("file:")!=0;
if (needToReload) {
window.setTimeout( function (browser) {
browser.addEventListener('load', SessionManager.afterTabLoad, true);
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
var _webNav = browser.webNavigation;
try {
var sh = _webNav.sessionHistory;
if (sh)
_webNav = sh.QueryInterface(nsIWebNavigation);
} catch (e) { }
try {
const flags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
_webNav.reload(flags);
} catch (e) { }
}, 0, aBrowser);
}
else
aBrowser.addEventListener('load', SessionManager.afterTabLoad, true);
webNav.gotoIndex(savedHistory.index);
} catch (e) {tmLog("error in loadOneTab gotoIndex ? ")}
}, // end of "loadOneTab : function(...............)"
afterTabLoad: function SM_afterTabLoad(event) {
var browser = this;
browser.removeEventListener('load', SessionManager.afterTabLoad, true);
var data = browser._sessionData;
var tab = gBrowser.getTabForBrowser(browser);
var tabExist = tab && tab.parentNode; // make sure tab was not removed
// restore scroll position
if (SessionPref.getBoolPref("save.scrollposition")) {
var XYZ = SessionManager.getLiteralValue(data.node, "scroll", "0,0,1");
if (XYZ != "0,0,1") {
XYZ = XYZ.split(",");
try {
var sHistory = browser.webNavigation.sessionHistory;
var curHistory = sHistory.getEntryAtIndex(sHistory.index, false);
curHistory.QueryInterface(tmSHEntry).setScrollPosition(XYZ[0], XYZ[1]);
} catch (ex) {TMP_ASSERT(ex, "loadOneTab error index " + sHistory.index); }
if (tabExist)
SessionManager.setScrollPosition(tab, browser, {href: null, _scrollX: XYZ[0], _scrollY: XYZ[1], zoom: XYZ[2] || 1}, 15);
}
}
if (tabExist) {
tab.removeAttribute("inrestore");
}
// call ensureTabIsVisible for the current tab
gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);
// check if we restore all tabs
if (--gBrowser.tabsToLoad == 0) {
delete gBrowser.tabsToLoad;
checkBeforeAndAfter(); // just in case (we do it also in setTabTitle
if (SessionManager.enableBackup){
var result = SessionManager.saveOneWindow(gSessionPath[0], "windowbackup");
if (result > 0)
SessionManager.saveStateDelayed(-1);
}
SessionManager.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "crash");
}
delete browser._sessionData;
},
setScrollPosition: function SM_setScrollPosition(aTab, aBrowser, aData, attempts) {
var bContent = aBrowser.contentWindow;
var docViewer;
// we don't use zoom after we drop support for Firefox 2.0 , so null the value
aData.zoom = null;
if (!aTab.hasAttribute("busy")) {
if (bContent.scrollX != aData._scrollX || bContent.scrollY != aData._scrollY)
bContent.scrollTo(aData._scrollX, aData._scrollY);
}
if (attempts && ( bContent.scrollX != aData._scrollX || bContent.scrollY != aData._scrollY)) {
window.setTimeout(SessionManager.setScrollPosition, 50, aTab, aBrowser, aData, --attempts);
return;
} else {
// if we save this before timeout sometimes scroll is not ready yet
if (SessionManager.enableBackup)
SessionManager.setLiteral(SessionManager.getNodeForTab(aTab), "scroll", aData._scrollX + "," + aData._scrollY + "," + aData.zoom);
// call by openLinkWithHistory
if (aData.href)
window.setTimeout( function(aBrowser, aURI) {
aBrowser.loadURI(aURI, null, null);
}, 0, aBrowser, aData.href);
}
},
loadTabHistory: function(rdfNodeSession, sHistoryInternal) {
var history = this.getLiteralValue(rdfNodeSession, "history");
var tmpData = history.split("|-|");
var sep = tmpData.shift(); // remove seperator from data
var historyData = tmpData.join("|-|").split(sep);
if (historyData.length < HSitems) {
tmLog("error in loadTabHistory" + "\n" + "historyData.length " + historyData.length + "\n" + "historyData " + historyData + "\n" + "history " + history);
return null; // if it les then 3 no data !!
}
if (typeof(sHistoryInternal) == "undefined")
sHistoryInternal = Components.classes["@mozilla.org/browser/shistory;1"]
.createInstance(tmSHistory);
sHistoryInternal = sHistoryInternal.QueryInterface(Components.interfaces.nsISHistoryInternal);
var sessionIndex = this.getIntValue(rdfNodeSession, "index");
var historyCount = historyData.length/HSitems;
if ( sessionIndex < 0 || sessionIndex >= historyCount ) sessionIndex = historyCount - 1;
var index, historyEntry, entryTitle, uriStr, newURI, XY;
for ( var i = 0; i < historyCount; i++ ){
index = i * HSitems;
if (!this.enableSaveHistory && sessionIndex != i) continue;
historyEntry = Components.classes["@mozilla.org/browser/session-history-entry;1"]
.createInstance(tmSHEntry);
entryTitle = unescape(historyData[index]);
uriStr = historyData[index + 1];
if (uriStr == "") uriStr = "about:blank";
newURI = gIOService.newURI(uriStr, null, null);
historyEntry.setTitle(entryTitle);
historyEntry.setURI(newURI);
historyEntry.saveLayoutStateFlag = true;
// xxx check if we can use this to reload tab not from Cache;
// is it good to reload ALL the history ???
if (SessionPref.getBoolPref("save.scrollposition")) {
if (historyData[index + 2] != "0,0") {
XY = historyData[index + 2].split(",");
historyEntry.setScrollPosition(XY[0], XY[1]); // XY is array [x,y]
}
}
sHistoryInternal.addEntry(historyEntry, true);
}
if (!this.enableSaveHistory) sessionIndex = 0;
return {history: sHistoryInternal, index: sessionIndex};
},
/**
* Back up and archive sessions
*/
archiveSessions: function SM_archiveSessions() {
var lastBackup = this.getMostRecentBackup();
// Backup Sessions if there aren't any backups or
// they haven't been backed up in the last 24 hrs.
const SESSIONS_ARCHIVE_INTERVAL = 86400 * 1000;
if (!lastBackup ||
Date.now() - lastBackup.lastModifiedTime > SESSIONS_ARCHIVE_INTERVAL) {
var maxBackups = 7;
// The maximum number of daily sessions backups to
// keep in <profile>/sessionbackups. Special values:
// -1: unlimited
// 0: no backups created (and deletes all existing backups)
// "extensions.tabmix.sessions.max_backups";
try {
maxBackups = SessionPref.getIntPref("max_backups");
} catch(ex) {}
this.archiveSessionsFile(maxBackups, false /* don't force */);
}
},
getSessionsBackupDir: function SM_getSessionsBackupDir(aCretate) {
var sessionsBackupDir = this.profileDir;
sessionsBackupDir.append("sessionbackups");
if (aCretate && !sessionsBackupDir.exists())
sessionsBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
return sessionsBackupDir;
},
/**
* Get the most recent backup file.
* @returns nsIFile backup file
*/
getMostRecentBackup: function SM_getMostRecentBackup() {
var sessionsBackupDir = this.getSessionsBackupDir(false);
if (!sessionsBackupDir.exists())
return null;
var backups = [];
var entries = sessionsBackupDir.directoryEntries;
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(Ci.nsIFile);
if (!entry.isHidden() && entry.leafName.match(/^tabmix_sessions-.+(rdf)?$/))
backups.push(entry.leafName);
}
if (backups.length == 0)
return null;
backups.sort();
var filename = backups.pop();
var backupFile = sessionsBackupDir.clone();
backupFile.append(filename);
return backupFile;
},
/**
* ArchiveSessionsFile()
*
* Creates a dated backup once a day in <profile>/sessionbackups.
*
* @param int aNumberOfBackups - the maximum number of backups to keep
*
* @param bool aForceArchive - forces creating an archive even if one was
* already created that day (overwrites)
*/
archiveSessionsFile:
function SM_archiveSessionsFile(aNumberOfBackups, aForceArchive) {
var sessionsBackupDir = this.getSessionsBackupDir(true);
if (!sessionsBackupDir.exists())
return; // unable to create directory!
// construct the new leafname
// Use YYYY-MM-DD (ISO 8601) as it doesn't contain illegal characters
// and makes the alphabetical order of multiple backup files more useful.
var d = new Date();
var date = [d.getFullYear(), '-', d.getMonth()<9 ? "0":"", d.getMonth()+1, '-', d.getDate()<10 ? "0":"", d.getDate()].join('');
var backupFilename = "tabmix_sessions-" + date + ".rdf"
var backupFile = null;
if (!aForceArchive) {
var backupFileNames = [];
var backupFilenamePrefix = backupFilename.substr(0, backupFilename.indexOf("-"));
var entries = sessionsBackupDir.directoryEntries;
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(Ci.nsIFile);
var backupName = entry.leafName;
if (backupName.substr(0, backupFilenamePrefix.length) == backupFilenamePrefix) {
if (backupName == backupFilename)
backupFile = entry;
backupFileNames.push(backupName);
}
}
var numberOfBackupsToDelete = 0;
if (aNumberOfBackups > -1)
numberOfBackupsToDelete = backupFileNames.length - aNumberOfBackups;
if (numberOfBackupsToDelete > 0) {
// If we don't have today's backup, remove one more so that
// the total backups after this operation does not exceed the
// number specified in the pref.
if (!backupFile)
numberOfBackupsToDelete++;
backupFileNames.sort();
while (numberOfBackupsToDelete--) {
backupFile = sessionsBackupDir.clone();
backupFile.append(backupFileNames[0]);
backupFile.remove(false);
backupFileNames.shift();
}
}
// do nothing if we either have today's backup already
// or the user has set the pref to zero.
if (backupFile || aNumberOfBackups == 0)
return;
}
backupFile = sessionsBackupDir.clone();
backupFile.append(backupFilename);
if (aForceArchive && backupFile.exists())
backupFile.remove(false);
if (!backupFile.exists()) {
var sessionsFile = this.profileDir;
sessionsFile.append("session.rdf");
if (sessionsFile.exists())
sessionsFile.copyTo(sessionsBackupDir, backupFilename);
}
}
};